From 56ce7c5aa4dff8d1a30fba070d5f7bc442e5f6ce Mon Sep 17 00:00:00 2001 From: merkost Date: Tue, 18 Jul 2023 12:32:06 +1000 Subject: [PATCH 01/16] Added settings migrating section --- app/src/main/res/layout/activity_settings.xml | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 99dfaa9b..154f95f9 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -367,6 +367,47 @@ android:text="@string/password_protect_whole_app" /> + + + + + + + + + + + + + + + + From 5363af1071adaee9a4a1947b5e62fa8c9250d45c Mon Sep 17 00:00:00 2001 From: merkost Date: Tue, 18 Jul 2023 12:38:31 +1000 Subject: [PATCH 02/16] Added serialization --- app/build.gradle | 12 ++++++++---- .../smsmessenger/models/MmsAddress.kt | 2 ++ .../smsmessenger/models/MmsBackup.kt | 2 ++ .../simplemobiletools/smsmessenger/models/MmsPart.kt | 2 ++ .../smsmessenger/models/SmsBackup.kt | 2 ++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7f9727a4..19659240 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,10 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -apply plugin: 'kotlin-kapt' +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-android-extensions' + id 'kotlin-kapt' + id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version" +} def keystorePropertiesFile = rootProject.file("keystore.properties") def keystoreProperties = new Properties() @@ -71,6 +74,7 @@ dependencies { implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'com.googlecode.ez-vcard:ez-vcard:0.11.3' implementation 'androidx.lifecycle:lifecycle-process:2.5.1' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1" kapt "androidx.room:room-compiler:2.5.1" implementation "androidx.room:room-runtime:2.5.1" diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt index 07148905..821047cc 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt @@ -4,7 +4,9 @@ import android.content.ContentValues import android.provider.Telephony import androidx.core.content.contentValuesOf import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable +@Serializable data class MmsAddress( @SerializedName("address") val address: String, diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt index e8957d93..c2c2c93c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt @@ -4,7 +4,9 @@ import android.content.ContentValues import android.provider.Telephony import androidx.core.content.contentValuesOf import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable +@Serializable data class MmsBackup( @SerializedName("creator") val creator: String?, diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt index ac5d53d1..8edde56e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt @@ -4,7 +4,9 @@ import android.content.ContentValues import android.provider.Telephony import androidx.core.content.contentValuesOf import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable +@Serializable data class MmsPart( @SerializedName("cd") val contentDisposition: String?, diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt index a6daa883..41b128e1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt @@ -5,7 +5,9 @@ import android.content.ContentValues import android.provider.Telephony import androidx.core.content.contentValuesOf import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable +@Serializable data class SmsBackup( @SerializedName("sub_id") val subscriptionId: Long, From 5a8cc0f14d33f15835fd9d8e32748c5d17a86298 Mon Sep 17 00:00:00 2001 From: merkost Date: Tue, 18 Jul 2023 16:21:44 +1000 Subject: [PATCH 03/16] Added BackupType with BackupSerializer --- .../smsmessenger/models/BackupType.kt | 13 +++++++++++ .../smsmessenger/models/MessagesBackup.kt | 23 +++++++++++++++++++ .../smsmessenger/models/MmsBackup.kt | 4 +++- .../smsmessenger/models/SmsBackup.kt | 6 +++-- 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/BackupType.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MessagesBackup.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/BackupType.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/BackupType.kt new file mode 100644 index 00000000..c6132aee --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/BackupType.kt @@ -0,0 +1,13 @@ +package com.simplemobiletools.smsmessenger.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +enum class BackupType { + @SerialName("sms") + SMS, + + @SerialName("mms") + MMS, +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MessagesBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MessagesBackup.kt new file mode 100644 index 00000000..9f97aac2 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MessagesBackup.kt @@ -0,0 +1,23 @@ +package com.simplemobiletools.smsmessenger.models + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.* + +@Serializable(with = BackupSerializer::class) +sealed class MessagesBackup() { + @SerialName("backupType") + abstract val backupType: BackupType +} + +object BackupSerializer : + JsonContentPolymorphicSerializer(MessagesBackup::class) { + override fun selectDeserializer(element: JsonElement): DeserializationStrategy { + return when (element.jsonObject["backupType"]?.jsonPrimitive?.content) { + "sms" -> SmsBackup.serializer() + "mms" -> MmsBackup.serializer() + else -> throw Exception("ERROR: No Serializer found. Serialization failed.") + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt index c2c2c93c..3d0c9d25 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt @@ -46,7 +46,9 @@ data class MmsBackup( val addresses: List, @SerializedName("parts") val parts: List, -) { + + override val backupType: BackupType = BackupType.MMS, +): MessagesBackup() { fun toContentValues(): ContentValues { return contentValuesOf( diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt index 41b128e1..ff3c9a39 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt @@ -30,8 +30,10 @@ data class SmsBackup( @SerializedName("type") val type: Int, @SerializedName("service_center") - val serviceCenter: String? -) { + val serviceCenter: String?, + + override val backupType: BackupType = BackupType.SMS, + ): MessagesBackup() { fun toContentValues(): ContentValues { return contentValuesOf( From 30b100b62f2e2635b196a3c1e140e334c0c07898 Mon Sep 17 00:00:00 2001 From: merkost Date: Tue, 18 Jul 2023 16:22:14 +1000 Subject: [PATCH 04/16] Dialogs refactoring --- .../dialogs/ExportMessagesDialog.kt | 79 ++++++------------- .../dialogs/ImportMessagesDialog.kt | 15 ++-- .../res/layout/dialog_export_messages.xml | 15 ---- 3 files changed, 34 insertions(+), 75 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt index 56c9791d..48bb8d5f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt @@ -2,76 +2,49 @@ package com.simplemobiletools.smsmessenger.dialogs import android.view.ViewGroup import androidx.appcompat.app.AlertDialog -import com.simplemobiletools.commons.dialogs.FilePickerDialog -import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.extensions.getAlertDialogBuilder +import com.simplemobiletools.commons.extensions.getCurrentFormattedDateTime +import com.simplemobiletools.commons.extensions.isAValidFilename +import com.simplemobiletools.commons.extensions.setupDialogStuff +import com.simplemobiletools.commons.extensions.toast +import com.simplemobiletools.commons.extensions.value import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.activities.SimpleActivity import com.simplemobiletools.smsmessenger.extensions.config -import com.simplemobiletools.smsmessenger.helpers.EXPORT_FILE_EXT -import kotlinx.android.synthetic.main.dialog_export_messages.view.* -import java.io.File +import kotlinx.android.synthetic.main.dialog_export_messages.view.export_messages_filename +import kotlinx.android.synthetic.main.dialog_export_messages.view.export_mms_checkbox +import kotlinx.android.synthetic.main.dialog_export_messages.view.export_sms_checkbox class ExportMessagesDialog( private val activity: SimpleActivity, - private val path: String, - private val hidePath: Boolean, - private val callback: (file: File) -> Unit, + private val callback: (fileName: String) -> Unit, ) { - private var realPath = if (path.isEmpty()) activity.internalStoragePath else path private val config = activity.config init { val view = (activity.layoutInflater.inflate(R.layout.dialog_export_messages, null) as ViewGroup).apply { - export_messages_folder.setText(activity.humanizePath(realPath)) - export_messages_filename.setText("${activity.getString(R.string.messages)}_${activity.getCurrentFormattedDateTime()}") export_sms_checkbox.isChecked = config.exportSms export_mms_checkbox.isChecked = config.exportMms + export_messages_filename.setText("${activity.getString(R.string.messages)}_${activity.getCurrentFormattedDateTime()}") + } - if (hidePath) { - export_messages_folder_hint.beGone() - } else { - export_messages_folder.setOnClickListener { - activity.hideKeyboard(export_messages_filename) - FilePickerDialog(activity, realPath, false, showFAB = true) { - export_messages_folder.setText(activity.humanizePath(it)) - realPath = it + activity.getAlertDialogBuilder().setPositiveButton(R.string.ok, null).setNegativeButton(R.string.cancel, null).apply { + activity.setupDialogStuff(view, this, R.string.export_messages) { alertDialog -> + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + config.exportSms = view.export_sms_checkbox.isChecked + config.exportMms = view.export_mms_checkbox.isChecked + val filename = view.export_messages_filename.value + when { + filename.isEmpty() -> activity.toast(R.string.empty_name) + filename.isAValidFilename() -> { + callback(filename) + alertDialog.dismiss() + } + + else -> activity.toast(R.string.invalid_name) } } } } - - activity.getAlertDialogBuilder() - .setPositiveButton(R.string.ok, null) - .setNegativeButton(R.string.cancel, null) - .apply { - activity.setupDialogStuff(view, this, R.string.export_messages) { alertDialog -> - alertDialog.showKeyboard(view.export_messages_filename) - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - val filename = view.export_messages_filename.value - when { - filename.isEmpty() -> activity.toast(R.string.empty_name) - filename.isAValidFilename() -> { - val file = File(realPath, "$filename$EXPORT_FILE_EXT") - if (!hidePath && file.exists()) { - activity.toast(R.string.name_taken) - return@setOnClickListener - } - - if (!view.export_sms_checkbox.isChecked && !view.export_mms_checkbox.isChecked) { - activity.toast(R.string.no_option_selected) - return@setOnClickListener - } - - config.exportSms = view.export_sms_checkbox.isChecked - config.exportMms = view.export_mms_checkbox.isChecked - config.lastExportPath = file.absolutePath.getParentPath() - callback(file) - alertDialog.dismiss() - } - else -> activity.toast(R.string.invalid_name) - } - } - } - } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt index 3a493dc6..24828490 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -10,13 +10,13 @@ import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.activities.SimpleActivity import com.simplemobiletools.smsmessenger.extensions.config import com.simplemobiletools.smsmessenger.helpers.MessagesImporter -import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_OK -import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_PARTIAL +import com.simplemobiletools.smsmessenger.models.MessagesBackup +import com.simplemobiletools.smsmessenger.models.ImportResult import kotlinx.android.synthetic.main.dialog_import_messages.view.* class ImportMessagesDialog( private val activity: SimpleActivity, - private val path: String, + private val messages: List, ) { private val config = activity.config @@ -48,7 +48,7 @@ class ImportMessagesDialog( config.importSms = view.import_sms_checkbox.isChecked config.importMms = view.import_mms_checkbox.isChecked ensureBackgroundThread { - MessagesImporter(activity).importMessages(path) { + MessagesImporter(activity).importMessages(messages) { handleParseResult(it) alertDialog.dismiss() } @@ -58,11 +58,12 @@ class ImportMessagesDialog( } } - private fun handleParseResult(result: MessagesImporter.ImportResult) { + private fun handleParseResult(result: ImportResult) { activity.toast( when (result) { - IMPORT_OK -> R.string.importing_successful - IMPORT_PARTIAL -> R.string.importing_some_entries_failed + ImportResult.IMPORT_OK -> R.string.importing_successful + ImportResult.IMPORT_PARTIAL -> R.string.importing_some_entries_failed + ImportResult.IMPORT_FAIL -> R.string.importing_failed else -> R.string.no_items_found } ) diff --git a/app/src/main/res/layout/dialog_export_messages.xml b/app/src/main/res/layout/dialog_export_messages.xml index 7fb85583..55747ed5 100644 --- a/app/src/main/res/layout/dialog_export_messages.xml +++ b/app/src/main/res/layout/dialog_export_messages.xml @@ -14,21 +14,6 @@ android:paddingTop="@dimen/activity_margin" android:paddingEnd="@dimen/activity_margin"> - - - - - - Date: Tue, 18 Jul 2023 16:23:04 +1000 Subject: [PATCH 05/16] ImportResult extracted to a separate class and MainActivity cleared --- .../smsmessenger/activities/MainActivity.kt | 119 ------------------ .../smsmessenger/helpers/MessagesExporter.kt | 68 ---------- .../smsmessenger/models/ImportResult.kt | 5 + app/src/main/res/menu/menu_main.xml | 10 -- 4 files changed, 5 insertions(+), 197 deletions(-) delete mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ImportResult.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt index 1204d748..07f056c0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -3,19 +3,15 @@ package com.simplemobiletools.smsmessenger.activities import android.annotation.SuppressLint import android.app.Activity import android.app.role.RoleManager -import android.content.ActivityNotFoundException import android.content.Intent import android.content.pm.ShortcutInfo import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.graphics.drawable.LayerDrawable -import android.net.Uri import android.os.Bundle import android.provider.Telephony import android.text.TextUtils -import android.widget.Toast import androidx.coordinatorlayout.widget.CoordinatorLayout -import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.dialogs.PermissionRequiredDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* @@ -25,8 +21,6 @@ import com.simplemobiletools.smsmessenger.BuildConfig import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.adapters.ConversationsAdapter import com.simplemobiletools.smsmessenger.adapters.SearchResultsAdapter -import com.simplemobiletools.smsmessenger.dialogs.ExportMessagesDialog -import com.simplemobiletools.smsmessenger.dialogs.ImportMessagesDialog import com.simplemobiletools.smsmessenger.extensions.* import com.simplemobiletools.smsmessenger.helpers.* import com.simplemobiletools.smsmessenger.models.Conversation @@ -37,19 +31,14 @@ import kotlinx.android.synthetic.main.activity_main.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -import java.io.FileOutputStream -import java.io.OutputStream class MainActivity : SimpleActivity() { private val MAKE_DEFAULT_APP_REQUEST = 1 - private val PICK_IMPORT_SOURCE_INTENT = 11 - private val PICK_EXPORT_FILE_INTENT = 21 private var storedTextColor = 0 private var storedFontSize = 0 private var lastSearchedText = "" private var bus: EventBus? = null - private val smsExporter by lazy { MessagesExporter(this) } private var wasProtectionHandled = false @SuppressLint("InlinedApi") @@ -174,8 +163,6 @@ class MainActivity : SimpleActivity() { main_menu.getToolbar().setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { - R.id.import_messages -> tryImportMessages() - R.id.export_messages -> tryToExportMessages() R.id.more_apps_from_us -> launchMoreAppsFromUsIntent() R.id.settings -> launchSettings() R.id.about -> launchAbout() @@ -199,11 +186,6 @@ class MainActivity : SimpleActivity() { } else { finish() } - } else if (requestCode == PICK_IMPORT_SOURCE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) { - tryImportMessagesFromFile(resultData.data!!) - } else if (requestCode == PICK_EXPORT_FILE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) { - val outputStream = contentResolver.openOutputStream(resultData.data!!) - exportMessagesTo(outputStream) } } @@ -578,107 +560,6 @@ class MainActivity : SimpleActivity() { startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true) } - private fun tryToExportMessages() { - if (isQPlus()) { - ExportMessagesDialog(this, config.lastExportPath, true) { file -> - Intent(Intent.ACTION_CREATE_DOCUMENT).apply { - type = EXPORT_MIME_TYPE - putExtra(Intent.EXTRA_TITLE, file.name) - addCategory(Intent.CATEGORY_OPENABLE) - - try { - startActivityForResult(this, PICK_EXPORT_FILE_INTENT) - } catch (e: ActivityNotFoundException) { - toast(R.string.system_service_disabled, Toast.LENGTH_LONG) - } catch (e: Exception) { - showErrorToast(e) - } - } - } - } else { - handlePermission(PERMISSION_WRITE_STORAGE) { - if (it) { - ExportMessagesDialog(this, config.lastExportPath, false) { file -> - getFileOutputStream(file.toFileDirItem(this), true) { outStream -> - exportMessagesTo(outStream) - } - } - } - } - } - } - - private fun exportMessagesTo(outputStream: OutputStream?) { - toast(R.string.exporting) - ensureBackgroundThread { - smsExporter.exportMessages(outputStream) { - val toastId = when (it) { - MessagesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful - else -> R.string.exporting_failed - } - - toast(toastId) - } - } - } - - private fun tryImportMessages() { - if (isQPlus()) { - Intent(Intent.ACTION_GET_CONTENT).apply { - addCategory(Intent.CATEGORY_OPENABLE) - type = EXPORT_MIME_TYPE - - try { - startActivityForResult(this, PICK_IMPORT_SOURCE_INTENT) - } catch (e: ActivityNotFoundException) { - toast(R.string.system_service_disabled, Toast.LENGTH_LONG) - } catch (e: Exception) { - showErrorToast(e) - } - } - } else { - handlePermission(PERMISSION_READ_STORAGE) { - if (it) { - importEvents() - } - } - } - } - - private fun importEvents() { - FilePickerDialog(this) { - showImportEventsDialog(it) - } - } - - private fun showImportEventsDialog(path: String) { - ImportMessagesDialog(this, path) - } - - private fun tryImportMessagesFromFile(uri: Uri) { - when (uri.scheme) { - "file" -> showImportEventsDialog(uri.path!!) - "content" -> { - val tempFile = getTempFile("messages", "backup.json") - if (tempFile == null) { - toast(R.string.unknown_error_occurred) - return - } - - try { - val inputStream = contentResolver.openInputStream(uri) - val out = FileOutputStream(tempFile) - inputStream!!.copyTo(out) - showImportEventsDialog(tempFile.absolutePath) - } catch (e: Exception) { - showErrorToast(e) - } - } - - else -> toast(R.string.invalid_file_format) - } - } - @Subscribe(threadMode = ThreadMode.MAIN) fun refreshMessages(event: Events.RefreshMessages) { initMessenger() diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt deleted file mode 100644 index 49c2d4b6..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.simplemobiletools.smsmessenger.helpers - -import android.content.Context -import com.google.gson.Gson -import com.google.gson.stream.JsonWriter -import com.simplemobiletools.commons.helpers.ensureBackgroundThread -import com.simplemobiletools.smsmessenger.extensions.config -import com.simplemobiletools.smsmessenger.extensions.getConversationIds -import java.io.OutputStream - -class MessagesExporter(private val context: Context) { - enum class ExportResult { - EXPORT_FAIL, EXPORT_OK - } - - private val config = context.config - private val messageReader = MessagesReader(context) - private val gson = Gson() - - fun exportMessages(outputStream: OutputStream?, onProgress: (total: Int, current: Int) -> Unit = { _, _ -> }, callback: (result: ExportResult) -> Unit) { - ensureBackgroundThread { - if (outputStream == null) { - callback.invoke(ExportResult.EXPORT_FAIL) - return@ensureBackgroundThread - } - val writer = JsonWriter(outputStream.bufferedWriter()) - writer.use { - try { - var written = 0 - writer.beginArray() - val conversationIds = context.getConversationIds() - val totalMessages = messageReader.getMessagesCount() - for (threadId in conversationIds) { - writer.beginObject() - if (config.exportSms && messageReader.getSmsCount() > 0) { - writer.name("sms") - writer.beginArray() - messageReader.forEachSms(threadId) { - writer.jsonValue(gson.toJson(it)) - written++ - onProgress.invoke(totalMessages, written) - } - writer.endArray() - } - - if (config.exportMms && messageReader.getMmsCount() > 0) { - writer.name("mms") - writer.beginArray() - messageReader.forEachMms(threadId) { - writer.jsonValue(gson.toJson(it)) - written++ - onProgress.invoke(totalMessages, written) - } - - writer.endArray() - } - - writer.endObject() - } - writer.endArray() - callback.invoke(ExportResult.EXPORT_OK) - } catch (e: Exception) { - callback.invoke(ExportResult.EXPORT_FAIL) - } - } - } - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ImportResult.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ImportResult.kt new file mode 100644 index 00000000..c4151d15 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ImportResult.kt @@ -0,0 +1,5 @@ +package com.simplemobiletools.smsmessenger.models + +enum class ImportResult { + IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL, IMPORT_NOTHING_NEW +} diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index d60f5871..0c9486f7 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -3,16 +3,6 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:ignore="AppCompatResource,AlwaysShowAction"> - - Date: Tue, 18 Jul 2023 16:23:46 +1000 Subject: [PATCH 06/16] MessagesImporter and MessagesReader refactoring --- .../smsmessenger/helpers/MessagesImporter.kt | 92 ++------- .../smsmessenger/helpers/MessagesReader.kt | 178 ++++++++++-------- 2 files changed, 114 insertions(+), 156 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt index 7377d057..c5020e31 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -1,102 +1,46 @@ package com.simplemobiletools.smsmessenger.helpers 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.helpers.ensureBackgroundThread import com.simplemobiletools.smsmessenger.extensions.config -import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_FAIL -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 +import com.simplemobiletools.smsmessenger.models.* 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 config = context.config private var messagesImported = 0 private var messagesFailed = 0 - fun importMessages(path: String, onProgress: (total: Int, current: Int) -> Unit = { _, _ -> }, callback: (result: ImportResult) -> Unit) { + fun importMessages(messagesBackup: List, callback: (result: ImportResult) -> Unit) { ensureBackgroundThread { try { - val inputStream = if (path.contains("/")) { - File(path).inputStream() - } else { - context.assets.open(path) - } - - inputStream.bufferedReader().use { reader -> - val jsonReader = gson.newJsonReader(reader) - val smsMessageType = object : TypeToken() {}.type - val mmsMessageType = object : TypeToken() {}.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(jsonReader, smsMessageType) - messageWriter.writeSmsMessage(message) - } else { - val message = gson.fromJson(jsonReader, mmsMessageType) - messageWriter.writeMmsMessage(message) - } - - messagesImported++ - } catch (e: Exception) { - context.showErrorToast(e) - messagesFailed++ - - } - } - jsonReader.endArray() - } else { - jsonReader.skipValue() - } + messagesBackup.forEach { message -> + try { + if (message.backupType == BackupType.SMS && config.importSms) { + messageWriter.writeSmsMessage(message as SmsBackup) + } else if (message.backupType == BackupType.MMS && config.importMms) { + messageWriter.writeMmsMessage(message as MmsBackup) } - - jsonReader.endObject() - refreshMessages() + else return@forEach + messagesImported++ + } catch (e: Exception) { + context.showErrorToast(e) + messagesFailed++ } - - jsonReader.endArray() } + refreshMessages() } catch (e: Exception) { context.showErrorToast(e) - messagesFailed++ } callback.invoke( when { - messagesImported == 0 && messagesFailed == 0 -> IMPORT_NOTHING_NEW - messagesFailed > 0 && messagesImported > 0 -> IMPORT_PARTIAL - messagesFailed > 0 -> IMPORT_FAIL - else -> IMPORT_OK + messagesImported == 0 && messagesFailed == 0 -> ImportResult.IMPORT_NOTHING_NEW + messagesFailed > 0 && messagesImported > 0 -> ImportResult.IMPORT_PARTIAL + messagesFailed > 0 -> ImportResult.IMPORT_FAIL + else -> ImportResult.IMPORT_OK } ) } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt index 175ad735..fbf164c5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -9,49 +9,60 @@ import android.util.Base64 import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.isQPlus import com.simplemobiletools.commons.helpers.isRPlus -import com.simplemobiletools.smsmessenger.models.MmsAddress -import com.simplemobiletools.smsmessenger.models.MmsBackup -import com.simplemobiletools.smsmessenger.models.MmsPart -import com.simplemobiletools.smsmessenger.models.SmsBackup +import com.simplemobiletools.smsmessenger.extensions.getConversationIds +import com.simplemobiletools.smsmessenger.models.* import java.io.IOException import java.io.InputStream class MessagesReader(private val context: Context) { - fun forEachSms(threadId: Long, block: (SmsBackup) -> Unit) { + + fun getMessagesToExport( + getSms: Boolean, + getMms: Boolean, + callback: (messages: List) -> Unit + ) { + val conversationIds = context.getConversationIds() + var smsMessages = listOf() + var mmsMessages = listOf() + + if (getSms) { + smsMessages = getSmsMessages(conversationIds) + } + if (getMms) { + mmsMessages = getMmsMessages(conversationIds) + } + callback(smsMessages + mmsMessages) + } + + private fun getSmsMessages(threadIds: List): List { val projection = arrayOf( - 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.SUBSCRIPTION_ID, 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 selectionArgs = arrayOf(threadId.toString()) - context.queryCursor(Sms.CONTENT_URI, projection, selection, selectionArgs) { cursor -> - val subscriptionId = cursor.getLongValue(Sms.SUBSCRIPTION_ID) - val address = cursor.getStringValue(Sms.ADDRESS) - val body = cursor.getStringValueOrNull(Sms.BODY) - val date = cursor.getLongValue(Sms.DATE) - val dateSent = cursor.getLongValue(Sms.DATE_SENT) - val locked = cursor.getIntValue(Sms.DATE_SENT) - val protocol = cursor.getStringValueOrNull(Sms.PROTOCOL) - val read = cursor.getIntValue(Sms.READ) - val status = cursor.getIntValue(Sms.STATUS) - val type = cursor.getIntValue(Sms.TYPE) - val serviceCenter = cursor.getStringValueOrNull(Sms.SERVICE_CENTER) - block(SmsBackup(subscriptionId, address, body, date, dateSent, locked, protocol, read, status, type, serviceCenter)) + val smsList = mutableListOf() + + threadIds.map { it.toString() }.forEach { threadId -> + context.queryCursor(Sms.CONTENT_URI, projection, selection, arrayOf(threadId)) { cursor -> + val subscriptionId = cursor.getLongValue(Sms.SUBSCRIPTION_ID) + val address = cursor.getStringValue(Sms.ADDRESS) + val body = cursor.getStringValueOrNull(Sms.BODY) + val date = cursor.getLongValue(Sms.DATE) + val dateSent = cursor.getLongValue(Sms.DATE_SENT) + val locked = cursor.getIntValue(Sms.DATE_SENT) + val protocol = cursor.getStringValueOrNull(Sms.PROTOCOL) + val read = cursor.getIntValue(Sms.READ) + val status = cursor.getIntValue(Sms.STATUS) + 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 - fun forEachMms(threadId: Long, includeTextOnlyAttachment: Boolean = false, block: (MmsBackup) -> Unit) { + private fun getMmsMessages(threadIds: List, includeTextOnlyAttachment: Boolean = false): List { val projection = arrayOf( Mms._ID, Mms.CREATOR, @@ -71,65 +82,67 @@ class MessagesReader(private val context: Context) { Mms.SUBSCRIPTION_ID, Mms.TRANSACTION_ID ) - val selection = if (includeTextOnlyAttachment) { "${Mms.THREAD_ID} = ? AND ${Mms.TEXT_ONLY} = ?" } else { "${Mms.THREAD_ID} = ?" } + val mmsList = mutableListOf() - val selectionArgs = if (includeTextOnlyAttachment) { - arrayOf(threadId.toString(), "1") - } else { - arrayOf(threadId.toString()) - } + threadIds.map { it.toString() }.forEach { threadId -> + val selectionArgs = if (includeTextOnlyAttachment) { + arrayOf(threadId, "1") + } 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 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) - - val parts = getParts(mmsId) - val addresses = getMmsAddresses(mmsId) - block( - MmsBackup( - creator, - contentType, - deliveryReport, - date, - dateSent, - locked, - messageType, - messageBox, - read, - readReport, - seen, - textOnly, - status, - subject, - subjectCharSet, - subscriptionId, - transactionId, - addresses, - parts + val parts = getParts(mmsId) + val addresses = getMmsAddresses(mmsId) + mmsList.add( + MmsBackup( + creator, + contentType, + deliveryReport, + date, + dateSent, + locked, + messageType, + messageBox, + read, + readReport, + seen, + textOnly, + status, + subject, + subjectCharSet, + subscriptionId, + transactionId, + addresses, + parts + ) ) - ) + } } + return mmsList } @SuppressLint("NewApi") @@ -172,6 +185,7 @@ class MessagesReader(private val context: Context) { stream.readBytes().toString(Charsets.UTF_8) } } + else -> { usePart(partId) { stream -> Base64.encodeToString(stream.readBytes(), Base64.DEFAULT) From 47866a1c19ea298e1020bace1fae04a84180c7b7 Mon Sep 17 00:00:00 2001 From: merkost Date: Tue, 18 Jul 2023 16:24:04 +1000 Subject: [PATCH 07/16] Settings activity configured import/export --- .../activities/SettingsActivity.kt | 90 ++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt index d97e67d0..cb697ece 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt @@ -2,21 +2,32 @@ package com.simplemobiletools.smsmessenger.activities import android.annotation.TargetApi import android.content.Intent +import android.net.Uri import android.os.Build import android.os.Bundle +import androidx.activity.result.contract.ActivityResultContracts import com.simplemobiletools.commons.activities.ManageBlockedNumbersActivity import com.simplemobiletools.commons.dialogs.* import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.smsmessenger.R +import com.simplemobiletools.smsmessenger.dialogs.ExportMessagesDialog +import com.simplemobiletools.smsmessenger.dialogs.ImportMessagesDialog import com.simplemobiletools.smsmessenger.extensions.config import com.simplemobiletools.smsmessenger.helpers.* +import com.simplemobiletools.smsmessenger.models.* import kotlinx.android.synthetic.main.activity_settings.* +import kotlinx.serialization.SerializationException +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import java.util.* +import kotlin.system.exitProcess class SettingsActivity : SimpleActivity() { private var blockedNumbersAtPause = -1 + private val messagesFileType = "application/json" override fun onCreate(savedInstanceState: Bundle?) { isMaterialActivity = true @@ -48,6 +59,8 @@ class SettingsActivity : SimpleActivity() { setupLockScreenVisibility() setupMMSFileSizeLimit() setupAppPasswordProtection() + setupMessagesExport() + setupMessagesImport() updateTextColors(settings_nested_scrollview) if (blockedNumbersAtPause != -1 && blockedNumbersAtPause != getBlockedNumbers().hashCode()) { @@ -59,12 +72,85 @@ class SettingsActivity : SimpleActivity() { settings_general_settings_label, settings_outgoing_messages_label, settings_notifications_label, - settings_security_label + settings_security_label, + settings_migrating_label ).forEach { it.setTextColor(getProperPrimaryColor()) } } + private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> + if (uri != null) { + toast(R.string.importing) + importMessages(uri) + } + } + + private val saveDocument = registerForActivityResult(ActivityResultContracts.CreateDocument(messagesFileType)) { uri -> + if (uri != null) { + toast(R.string.exporting) + exportMessages(uri) + } + } + + private fun setupMessagesExport() { + settings_export_messages_holder.setOnClickListener { + ExportMessagesDialog(this) { fileName -> + saveDocument.launch(fileName) + } + } + } + + private fun setupMessagesImport() { + settings_import_messages_holder.setOnClickListener { + getContent.launch(messagesFileType) + } + } + + private fun exportMessages(uri: Uri) { + ensureBackgroundThread { + try { + MessagesReader(this).getMessagesToExport(config.exportSms, config.exportMms) { messagesToExport -> + if (messagesToExport.isEmpty()) { + toast(R.string.no_entries_for_exporting) + return@getMessagesToExport + } + val json = Json { encodeDefaults = true } + val jsonString = json.encodeToString(messagesToExport) + val outputStream = contentResolver.openOutputStream(uri)!! + + outputStream.use { + it.write(jsonString.toByteArray()) + } + toast(R.string.exporting_successful) + } + } catch (e: Exception) { + showErrorToast(e) + } + } + } + + private fun importMessages(uri: Uri) { + try { + val jsonString = contentResolver.openInputStream(uri)!!.use { inputStream -> + inputStream.bufferedReader().readText() + } + + val deserializedList = Json.decodeFromString>(jsonString) + if (deserializedList.isEmpty()) { + toast(R.string.no_entries_for_importing) + return + } + ImportMessagesDialog(this, deserializedList) + } catch (e: SerializationException) { + toast(R.string.invalid_file_format) + } catch (e: IllegalArgumentException) { + toast(R.string.invalid_file_format) + } catch (e: Exception) { + showErrorToast(e) + } + } + override fun onPause() { super.onPause() blockedNumbersAtPause = getBlockedNumbers().hashCode() @@ -97,7 +183,7 @@ class SettingsActivity : SimpleActivity() { settings_use_english_holder.setOnClickListener { settings_use_english.toggle() config.useEnglish = settings_use_english.isChecked - System.exit(0) + exitProcess(0) } } From db5decfcd888650c22343f3f1a5230b5bebe9f10 Mon Sep 17 00:00:00 2001 From: merkost Date: Tue, 18 Jul 2023 17:17:52 +1000 Subject: [PATCH 08/16] Added additional types for txt or xml and moved import logic to Importer --- .../activities/SettingsActivity.kt | 32 +--- .../dialogs/ImportMessagesDialog.kt | 2 +- .../smsmessenger/helpers/MessagesImporter.kt | 157 +++++++++++++++++- 3 files changed, 158 insertions(+), 33 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt index 310c81f9..47aac9c5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt @@ -28,6 +28,7 @@ import kotlin.system.exitProcess class SettingsActivity : SimpleActivity() { private var blockedNumbersAtPause = -1 private val messagesFileType = "application/json" + private val messageImportFileType = "application/json, application/xml, text/plain" override fun onCreate(savedInstanceState: Bundle?) { isMaterialActivity = true @@ -83,7 +84,13 @@ class SettingsActivity : SimpleActivity() { private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> if (uri != null) { toast(R.string.importing) - importMessages(uri) + MessagesImporter(this).importMessages(uri) { deserializedList -> + if (deserializedList.isEmpty()) { + toast(R.string.no_entries_for_importing) + } else { + ImportMessagesDialog(this, deserializedList) + } + } } } @@ -104,7 +111,7 @@ class SettingsActivity : SimpleActivity() { private fun setupMessagesImport() { settings_import_messages_holder.setOnClickListener { - getContent.launch(messagesFileType) + getContent.launch(messageImportFileType) } } @@ -131,27 +138,6 @@ class SettingsActivity : SimpleActivity() { } } - private fun importMessages(uri: Uri) { - try { - val jsonString = contentResolver.openInputStream(uri)!!.use { inputStream -> - inputStream.bufferedReader().readText() - } - - val deserializedList = Json.decodeFromString>(jsonString) - if (deserializedList.isEmpty()) { - toast(R.string.no_entries_for_importing) - return - } - ImportMessagesDialog(this, deserializedList) - } catch (e: SerializationException) { - toast(R.string.invalid_file_format) - } catch (e: IllegalArgumentException) { - toast(R.string.invalid_file_format) - } catch (e: Exception) { - showErrorToast(e) - } - } - override fun onPause() { super.onPause() blockedNumbersAtPause = getBlockedNumbers().hashCode() diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt index 24828490..626115fa 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -48,7 +48,7 @@ class ImportMessagesDialog( config.importSms = view.import_sms_checkbox.isChecked config.importMms = view.import_mms_checkbox.isChecked ensureBackgroundThread { - MessagesImporter(activity).importMessages(messages) { + MessagesImporter(activity).restoreMessages(messages) { handleParseResult(it) alertDialog.dismiss() } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt index 149c9489..8d04ec47 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -1,20 +1,68 @@ package com.simplemobiletools.smsmessenger.helpers -import android.content.Context +import android.net.Uri +import android.util.Xml import com.simplemobiletools.commons.extensions.showErrorToast +import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.helpers.ensureBackgroundThread +import com.simplemobiletools.smsmessenger.R +import com.simplemobiletools.smsmessenger.activities.SimpleActivity +import com.simplemobiletools.smsmessenger.dialogs.ImportMessagesDialog import com.simplemobiletools.smsmessenger.extensions.config import com.simplemobiletools.smsmessenger.models.* +import kotlinx.serialization.SerializationException +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import org.xmlpull.v1.XmlPullParser +import java.io.InputStream -class MessagesImporter(private val context: Context) { +class MessagesImporter(private val activity: SimpleActivity) { - private val messageWriter = MessagesWriter(context) - private val config = context.config + private val messageWriter = MessagesWriter(activity) + private val config = activity.config private var messagesImported = 0 private var messagesFailed = 0 - fun importMessages(messagesBackup: List, callback: (result: ImportResult) -> Unit) { + fun importMessages(uri: Uri) { + try { + val fileType = activity.contentResolver.getType(uri).orEmpty() + + val isXml = isXmlMimeType(fileType) || (uri.path?.endsWith("txt") == true && isFileXml(uri)) + + if (isXml) { + getInputStreamFromUri(uri)!!.importXml() + } else { + importJson(uri) + } + + } catch (e: Exception) { + activity.showErrorToast(e) + } + } + + private fun importJson(uri: Uri) { + try { + val jsonString = activity.contentResolver.openInputStream(uri)!!.use { inputStream -> + inputStream.bufferedReader().readText() + } + + val deserializedList = Json.decodeFromString>(jsonString) + if (deserializedList.isEmpty()) { + activity.toast(R.string.no_entries_for_importing) + return + } + ImportMessagesDialog(activity, deserializedList) + } catch (e: SerializationException) { + activity.toast(R.string.invalid_file_format) + } catch (e: IllegalArgumentException) { + activity.toast(R.string.invalid_file_format) + } catch (e: Exception) { + activity.showErrorToast(e) + } + } + + fun restoreMessages(messagesBackup: List, callback: (ImportResult) -> Unit) { ensureBackgroundThread { try { messagesBackup.forEach { message -> @@ -23,17 +71,16 @@ class MessagesImporter(private val context: Context) { messageWriter.writeSmsMessage(message as SmsBackup) } else if (message.backupType == BackupType.MMS && config.importMms) { messageWriter.writeMmsMessage(message as MmsBackup) - } - else return@forEach + } else return@forEach messagesImported++ } catch (e: Exception) { - context.showErrorToast(e) + activity.showErrorToast(e) messagesFailed++ } } refreshMessages() } catch (e: Exception) { - context.showErrorToast(e) + activity.showErrorToast(e) } callback.invoke( @@ -46,4 +93,96 @@ class MessagesImporter(private val context: Context) { ) } } + + private fun InputStream.importXml() { + bufferedReader().use { reader -> + val xmlParser = Xml.newPullParser().apply { + setInput(reader) + } + + xmlParser.nextTag() + xmlParser.require(XmlPullParser.START_TAG, null, "smses") + + var depth = 1 + while (depth != 0) { + when (xmlParser.next()) { + XmlPullParser.END_TAG -> depth-- + XmlPullParser.START_TAG -> depth++ + } + + if (xmlParser.eventType != XmlPullParser.START_TAG) { + continue + } + + try { + if (xmlParser.name == "sms") { + if (config.importSms) { + val message = xmlParser.readSms() + messageWriter.writeSmsMessage(message) + messagesImported++ + } else { + xmlParser.skip() + } + } else { + xmlParser.skip() + } + } catch (e: Exception) { + activity.showErrorToast(e) + messagesFailed++ + } + } + refreshMessages() + } + // TODO: Add result to xml import + } + + private fun XmlPullParser.readSms(): SmsBackup { + require(XmlPullParser.START_TAG, null, "sms") + + return SmsBackup( + subscriptionId = 0, + address = getAttributeValue(null, "address"), + body = getAttributeValue(null, "body"), + date = getAttributeValue(null, "date").toLong(), + dateSent = getAttributeValue(null, "date").toLong(), + locked = getAttributeValue(null, "locked").toInt(), + protocol = getAttributeValue(null, "protocol"), + read = getAttributeValue(null, "read").toInt(), + status = getAttributeValue(null, "status").toInt(), + type = getAttributeValue(null, "type").toInt(), + serviceCenter = getAttributeValue(null, "service_center") + ) + } + + private fun XmlPullParser.skip() { + if (eventType != XmlPullParser.START_TAG) { + throw IllegalStateException() + } + var depth = 1 + while (depth != 0) { + when (next()) { + XmlPullParser.END_TAG -> depth-- + XmlPullParser.START_TAG -> depth++ + } + } + } + + private fun getInputStreamFromUri(uri: Uri): InputStream? { + return try { + activity.contentResolver.openInputStream(uri) + } catch (e: Exception) { + null + } + } + + private fun isFileXml(uri: Uri): Boolean { + val inputStream = getInputStreamFromUri(uri) + return inputStream?.bufferedReader()?.use { reader -> + reader.readLine()?.startsWith(" Date: Thu, 20 Jul 2023 13:37:54 +1000 Subject: [PATCH 09/16] Added and refactored xml import support --- .../smsmessenger/activities/SettingsActivity.kt | 17 ++++------------- .../smsmessenger/helpers/MessagesImporter.kt | 15 ++++++++++----- .../smsmessenger/models/MessagesBackup.kt | 3 ++- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt index 47aac9c5..094f5e56 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt @@ -13,13 +13,10 @@ import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.dialogs.ExportMessagesDialog -import com.simplemobiletools.smsmessenger.dialogs.ImportMessagesDialog import com.simplemobiletools.smsmessenger.extensions.config import com.simplemobiletools.smsmessenger.helpers.* import com.simplemobiletools.smsmessenger.models.* import kotlinx.android.synthetic.main.activity_settings.* -import kotlinx.serialization.SerializationException -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.util.* @@ -28,7 +25,7 @@ import kotlin.system.exitProcess class SettingsActivity : SimpleActivity() { private var blockedNumbersAtPause = -1 private val messagesFileType = "application/json" - private val messageImportFileType = "application/json, application/xml, text/plain" + private val messageImportFileTypes = listOf("application/json", "application/xml", "text/xml") override fun onCreate(savedInstanceState: Bundle?) { isMaterialActivity = true @@ -81,16 +78,10 @@ class SettingsActivity : SimpleActivity() { } } - private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> + private val getContent = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> if (uri != null) { toast(R.string.importing) - MessagesImporter(this).importMessages(uri) { deserializedList -> - if (deserializedList.isEmpty()) { - toast(R.string.no_entries_for_importing) - } else { - ImportMessagesDialog(this, deserializedList) - } - } + MessagesImporter(this).importMessages(uri) } } @@ -111,7 +102,7 @@ class SettingsActivity : SimpleActivity() { private fun setupMessagesImport() { settings_import_messages_holder.setOnClickListener { - getContent.launch(messageImportFileType) + getContent.launch(messageImportFileTypes.toTypedArray()) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt index 8d04ec47..66eaff0c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -27,15 +27,12 @@ class MessagesImporter(private val activity: SimpleActivity) { fun importMessages(uri: Uri) { try { val fileType = activity.contentResolver.getType(uri).orEmpty() - val isXml = isXmlMimeType(fileType) || (uri.path?.endsWith("txt") == true && isFileXml(uri)) - if (isXml) { getInputStreamFromUri(uri)!!.importXml() } else { importJson(uri) } - } catch (e: Exception) { activity.showErrorToast(e) } @@ -133,7 +130,11 @@ class MessagesImporter(private val activity: SimpleActivity) { } refreshMessages() } - // TODO: Add result to xml import + when { + messagesFailed > 0 && messagesImported > 0 -> activity.toast(R.string.importing_some_entries_failed) + messagesFailed > 0 -> activity.toast(R.string.importing_failed) + else -> activity.toast(R.string.importing_successful) + } } private fun XmlPullParser.readSms(): SmsBackup { @@ -183,6 +184,10 @@ class MessagesImporter(private val activity: SimpleActivity) { } private fun isXmlMimeType(mimeType: String): Boolean { - return mimeType.equals("application/xml", ignoreCase = true) + return mimeType.equals("application/xml", ignoreCase = true) || mimeType.equals("text/xml", ignoreCase = true) + } + + private fun isJsonMimeType(mimeType: String): Boolean { + return mimeType.equals("application/json", ignoreCase = true) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MessagesBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MessagesBackup.kt index 9f97aac2..a01137c9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MessagesBackup.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MessagesBackup.kt @@ -3,6 +3,7 @@ package com.simplemobiletools.smsmessenger.models import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException import kotlinx.serialization.json.* @Serializable(with = BackupSerializer::class) @@ -17,7 +18,7 @@ object BackupSerializer : return when (element.jsonObject["backupType"]?.jsonPrimitive?.content) { "sms" -> SmsBackup.serializer() "mms" -> MmsBackup.serializer() - else -> throw Exception("ERROR: No Serializer found. Serialization failed.") + else -> throw SerializationException("ERROR: No Serializer found. Serialization failed.") } } } From c72dc199aac17a5326328b112721c800475f4223 Mon Sep 17 00:00:00 2001 From: merkost Date: Sat, 22 Jul 2023 00:10:17 +1000 Subject: [PATCH 10/16] ExportMessagesDialog code refactoring --- .../dialogs/ExportMessagesDialog.kt | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt index c3f5818f..d694b313 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt @@ -2,12 +2,7 @@ package com.simplemobiletools.smsmessenger.dialogs import android.view.ViewGroup import androidx.appcompat.app.AlertDialog -import com.simplemobiletools.commons.extensions.getAlertDialogBuilder -import com.simplemobiletools.commons.extensions.getCurrentFormattedDateTime -import com.simplemobiletools.commons.extensions.isAValidFilename -import com.simplemobiletools.commons.extensions.setupDialogStuff -import com.simplemobiletools.commons.extensions.toast -import com.simplemobiletools.commons.extensions.value +import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.activities.SimpleActivity import com.simplemobiletools.smsmessenger.extensions.config @@ -26,27 +21,32 @@ class ExportMessagesDialog( val view = (activity.layoutInflater.inflate(R.layout.dialog_export_messages, null) as ViewGroup).apply { export_sms_checkbox.isChecked = config.exportSms export_mms_checkbox.isChecked = config.exportMms - export_messages_filename.setText("${activity.getString(R.string.messages)}_${activity.getCurrentFormattedDateTime()}") + export_messages_filename.setText( + activity.getString(R.string.messages) + "_" + activity.getCurrentFormattedDateTime() + ) } - activity.getAlertDialogBuilder().setPositiveButton(R.string.ok, null).setNegativeButton(R.string.cancel, null).apply { - activity.setupDialogStuff(view, this, R.string.export_messages) { alertDialog -> - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - config.exportSms = view.export_sms_checkbox.isChecked - config.exportMms = view.export_mms_checkbox.isChecked - val filename = view.export_messages_filename.value - when { - filename.isEmpty() -> activity.toast(R.string.empty_name) - filename.isAValidFilename() -> { - callback(filename) - alertDialog.dismiss() + activity.getAlertDialogBuilder() + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.cancel, null) + .apply { + activity.setupDialogStuff(view, this, R.string.export_messages) { alertDialog -> + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + config.exportSms = view.export_sms_checkbox.isChecked + config.exportMms = view.export_mms_checkbox.isChecked + val filename = view.export_messages_filename.value + when { + filename.isEmpty() -> activity.toast(R.string.empty_name) + filename.isAValidFilename() -> { + callback(filename) + alertDialog.dismiss() + } + + else -> activity.toast(R.string.invalid_name) } - - else -> activity.toast(R.string.invalid_name) } } } - } } } From 842368d0f415d19682631a14a3a1ac74c4dc734c Mon Sep 17 00:00:00 2001 From: merkost Date: Sat, 22 Jul 2023 00:14:55 +1000 Subject: [PATCH 11/16] Split lines in MessagesReader --- .../smsmessenger/helpers/MessagesReader.kt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt index 809e3325..eacd4345 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -17,9 +17,7 @@ import java.io.InputStream class MessagesReader(private val context: Context) { fun getMessagesToExport( - getSms: Boolean, - getMms: Boolean, - callback: (messages: List) -> Unit + getSms: Boolean, getMms: Boolean, callback: (messages: List) -> Unit ) { val conversationIds = context.getConversationIds() var smsMessages = listOf() @@ -36,7 +34,17 @@ class MessagesReader(private val context: Context) { private fun getSmsMessages(threadIds: List): List { val projection = arrayOf( - 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.SUBSCRIPTION_ID, + 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} = ?" From 8c0508b0c1db9c50047f7910a39d1d1f8551d057 Mon Sep 17 00:00:00 2001 From: merkost Date: Sat, 22 Jul 2023 00:31:24 +1000 Subject: [PATCH 12/16] Refactored message restoring in MessagesImporter --- .../smsmessenger/helpers/MessagesImporter.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt index 66eaff0c..b3e625b8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -66,10 +66,11 @@ class MessagesImporter(private val activity: SimpleActivity) { try { if (message.backupType == BackupType.SMS && config.importSms) { messageWriter.writeSmsMessage(message as SmsBackup) + messagesImported++ } else if (message.backupType == BackupType.MMS && config.importMms) { messageWriter.writeMmsMessage(message as MmsBackup) - } else return@forEach - messagesImported++ + messagesImported++ + } } catch (e: Exception) { activity.showErrorToast(e) messagesFailed++ From 39ca2d60798e29ad50f4aff32fa712fc2387225f Mon Sep 17 00:00:00 2001 From: merkost Date: Sat, 22 Jul 2023 10:05:45 +1000 Subject: [PATCH 13/16] Added proguard-rules.pro for Kotlin Serialization --- app/proguard-rules.pro | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index e6c59860..2c52942d 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -4,3 +4,27 @@ @org.greenrobot.eventbus.Subscribe ; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } + +# Keep `Companion` object fields of serializable classes. +# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects. +-if @kotlinx.serialization.Serializable class ** +-keepclassmembers class <1> { + static <1>$Companion Companion; +} + +# Keep `serializer()` on companion objects (both default and named) of serializable classes. +-if @kotlinx.serialization.Serializable class ** { + static **$* *; +} +-keepclassmembers class <2>$<3> { + kotlinx.serialization.KSerializer serializer(...); +} + +# Keep `INSTANCE.serializer()` of serializable objects. +-if @kotlinx.serialization.Serializable class ** { + public static ** INSTANCE; +} +-keepclassmembers class <1> { + public static <1> INSTANCE; + kotlinx.serialization.KSerializer serializer(...); +} From e244fd5a53058ab3a180ab24c97af0465d8dd727 Mon Sep 17 00:00:00 2001 From: merkost Date: Sat, 22 Jul 2023 22:54:05 +1000 Subject: [PATCH 14/16] Moved "Importing" toast --- .../smsmessenger/activities/SettingsActivity.kt | 1 - .../simplemobiletools/smsmessenger/helpers/MessagesImporter.kt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt index 094f5e56..12c845f6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/SettingsActivity.kt @@ -80,7 +80,6 @@ class SettingsActivity : SimpleActivity() { private val getContent = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> if (uri != null) { - toast(R.string.importing) MessagesImporter(this).importMessages(uri) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt index b3e625b8..e9a70bf8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -29,6 +29,7 @@ class MessagesImporter(private val activity: SimpleActivity) { val fileType = activity.contentResolver.getType(uri).orEmpty() val isXml = isXmlMimeType(fileType) || (uri.path?.endsWith("txt") == true && isFileXml(uri)) if (isXml) { + activity.toast(R.string.importing) getInputStreamFromUri(uri)!!.importXml() } else { importJson(uri) From 014ac2a4cff3a1329f62479e2e45c1f0ef957ba0 Mon Sep 17 00:00:00 2001 From: merkost Date: Mon, 24 Jul 2023 13:40:38 +1000 Subject: [PATCH 15/16] Added invalid_file_format toast on XML importing --- .../smsmessenger/helpers/MessagesImporter.kt | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt index e9a70bf8..af95f391 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -94,48 +94,52 @@ class MessagesImporter(private val activity: SimpleActivity) { } private fun InputStream.importXml() { - bufferedReader().use { reader -> - val xmlParser = Xml.newPullParser().apply { - setInput(reader) - } - - xmlParser.nextTag() - xmlParser.require(XmlPullParser.START_TAG, null, "smses") - - var depth = 1 - while (depth != 0) { - when (xmlParser.next()) { - XmlPullParser.END_TAG -> depth-- - XmlPullParser.START_TAG -> depth++ + try { + bufferedReader().use { reader -> + val xmlParser = Xml.newPullParser().apply { + setInput(reader) } - if (xmlParser.eventType != XmlPullParser.START_TAG) { - continue - } + xmlParser.nextTag() + xmlParser.require(XmlPullParser.START_TAG, null, "smses") - try { - if (xmlParser.name == "sms") { - if (config.importSms) { - val message = xmlParser.readSms() - messageWriter.writeSmsMessage(message) - messagesImported++ + var depth = 1 + while (depth != 0) { + when (xmlParser.next()) { + XmlPullParser.END_TAG -> depth-- + XmlPullParser.START_TAG -> depth++ + } + + if (xmlParser.eventType != XmlPullParser.START_TAG) { + continue + } + + try { + if (xmlParser.name == "sms") { + if (config.importSms) { + val message = xmlParser.readSms() + messageWriter.writeSmsMessage(message) + messagesImported++ + } else { + xmlParser.skip() + } } else { xmlParser.skip() } - } else { - xmlParser.skip() + } catch (e: Exception) { + activity.showErrorToast(e) + messagesFailed++ } - } catch (e: Exception) { - activity.showErrorToast(e) - messagesFailed++ } + refreshMessages() } - refreshMessages() - } - when { - messagesFailed > 0 && messagesImported > 0 -> activity.toast(R.string.importing_some_entries_failed) - messagesFailed > 0 -> activity.toast(R.string.importing_failed) - else -> activity.toast(R.string.importing_successful) + when { + messagesFailed > 0 && messagesImported > 0 -> activity.toast(R.string.importing_some_entries_failed) + messagesFailed > 0 -> activity.toast(R.string.importing_failed) + else -> activity.toast(R.string.importing_successful) + } + } catch (_: Exception) { + activity.toast(R.string.invalid_file_format) } } From deecd786507b91e1661043384db8395f79abf9a1 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Mon, 24 Jul 2023 10:55:10 +0200 Subject: [PATCH 16/16] minor code style update --- .../smsmessenger/dialogs/ExportMessagesDialog.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt index d694b313..b25556e0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt @@ -10,7 +10,6 @@ import kotlinx.android.synthetic.main.dialog_export_messages.view.export_message import kotlinx.android.synthetic.main.dialog_export_messages.view.export_mms_checkbox import kotlinx.android.synthetic.main.dialog_export_messages.view.export_sms_checkbox - class ExportMessagesDialog( private val activity: SimpleActivity, private val callback: (fileName: String) -> Unit, @@ -40,7 +39,6 @@ class ExportMessagesDialog( filename.isAValidFilename() -> { callback(filename) alertDialog.dismiss() - } else -> activity.toast(R.string.invalid_name)