From 2a5f2bfc03092e2a363ab291c24c34e4c2813571 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 10 Sep 2021 00:08:57 +0100 Subject: [PATCH 01/29] Add dialog to export messages --- .../smsmessenger/activities/MainActivity.kt | 45 ++++++++++- .../dialogs/ExportMessagesDialog.kt | 76 +++++++++++++++++++ .../smsmessenger/helpers/Config.kt | 13 ++++ .../smsmessenger/helpers/Constants.kt | 5 ++ .../res/layout/dialog_export_messages.xml | 69 +++++++++++++++++ app/src/main/res/menu/menu_main.xml | 10 +++ app/src/main/res/values/strings.xml | 9 ++- 7 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt create mode 100644 app/src/main/res/layout/dialog_export_messages.xml 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 513e50ac..1368b3ab 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -18,19 +18,25 @@ import com.simplemobiletools.commons.models.FAQItem import com.simplemobiletools.smsmessenger.BuildConfig import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.adapters.ConversationsAdapter +import com.simplemobiletools.smsmessenger.dialogs.ExportMessagesDialog import com.simplemobiletools.smsmessenger.extensions.* +import com.simplemobiletools.smsmessenger.helpers.EXPORT_MIME_TYPE import com.simplemobiletools.smsmessenger.helpers.THREAD_ID import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE import com.simplemobiletools.smsmessenger.models.Conversation import com.simplemobiletools.smsmessenger.models.Events +import java.io.OutputStream +import java.util.ArrayList +import java.util.Arrays import kotlinx.android.synthetic.main.activity_main.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -import java.util.* 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 @@ -108,6 +114,8 @@ class MainActivity : SimpleActivity() { when (item.itemId) { R.id.search -> launchSearch() R.id.settings -> launchSettings() + R.id.export_messages -> tryToExportMessages() + R.id.import_messages -> launchSettings() R.id.about -> launchAbout() else -> return super.onOptionsItemSelected(item) } @@ -122,6 +130,11 @@ class MainActivity : SimpleActivity() { } else { finish() } + } else if (requestCode == PICK_IMPORT_SOURCE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) { + + } else if (requestCode == PICK_EXPORT_FILE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) { + val outputStream = contentResolver.openOutputStream(resultData.data!!) + exportMessagesTo(outputStream) } } @@ -318,6 +331,36 @@ 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) + startActivityForResult(this, PICK_EXPORT_FILE_INTENT) + } + } + } 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?) { + ensureBackgroundThread { + + } + } + @Subscribe(threadMode = ThreadMode.MAIN) fun refreshMessages(event: Events.RefreshMessages) { initMessenger() diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt new file mode 100644 index 00000000..5115cfbd --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt @@ -0,0 +1,76 @@ +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.helpers.ensureBackgroundThread +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 java.io.File +import kotlinx.android.synthetic.main.dialog_export_messages.view.* + +class ExportMessagesDialog( + private val activity: SimpleActivity, + private val path: String, + private val hidePath: Boolean, + private val callback: (file: File) -> 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.text = 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 + + if (hidePath) { + export_messages_folder_label.beGone() + export_messages_folder.beGone() + } else { + export_messages_folder.setOnClickListener { + activity.hideKeyboard(export_messages_filename) + FilePickerDialog(activity, realPath, false, showFAB = true) { + export_messages_folder.text = activity.humanizePath(it) + realPath = it + } + } + } + } + + AlertDialog.Builder(activity) + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.cancel, null) + .create().apply { + activity.setupDialogStuff(view, this, R.string.export_messages) { + 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 + } + + ensureBackgroundThread { + config.lastExportPath = file.absolutePath.getParentPath() + config.exportSms = view.export_sms_checkbox.isChecked + config.exportMms = view.export_mms_checkbox.isChecked + + callback(file) + dismiss() + } + } + else -> activity.toast(R.string.invalid_name) + } + } + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt index e284b832..87af0344 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt @@ -25,4 +25,17 @@ class Config(context: Context) : BaseConfig(context) { var lockScreenVisibilitySetting: Int get() = prefs.getInt(LOCK_SCREEN_VISIBILITY, LOCK_SCREEN_SENDER_MESSAGE) set(lockScreenVisibilitySetting) = prefs.edit().putInt(LOCK_SCREEN_VISIBILITY, lockScreenVisibilitySetting).apply() + + var lastExportPath: String + get() = prefs.getString(LAST_EXPORT_PATH, "")!! + set(lastExportPath) = prefs.edit().putString(LAST_EXPORT_PATH, lastExportPath).apply() + + var exportSms: Boolean + get() = prefs.getBoolean(EXPORT_SMS, true) + set(exportSms) = prefs.edit().putBoolean(EXPORT_SMS, exportSms).apply() + + var exportMms: Boolean + get() = prefs.getBoolean(EXPORT_MMS, true) + set(exportMms) = prefs.edit().putBoolean(EXPORT_MMS, exportMms).apply() + } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt index 36025476..04f4b988 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt @@ -15,6 +15,11 @@ const val NOTIFICATION_CHANNEL = "simple_sms_messenger" const val SHOW_CHARACTER_COUNTER = "show_character_counter" const val LOCK_SCREEN_VISIBILITY = "lock_screen_visibility" const val ENABLE_DELIVERY_REPORTS = "enable_delivery_reports" +const val LAST_EXPORT_PATH = "last_export_path" +const val EXPORT_SMS = "export_sms" +const val EXPORT_MMS = "export_mms" +const val EXPORT_MIME_TYPE = "application/json" +const val EXPORT_FILE_EXT = ".json" private const val PATH = "com.simplemobiletools.smsmessenger.action." const val MARK_AS_READ = PATH + "mark_as_read" diff --git a/app/src/main/res/layout/dialog_export_messages.xml b/app/src/main/res/layout/dialog_export_messages.xml new file mode 100644 index 00000000..1c2fda39 --- /dev/null +++ b/app/src/main/res/layout/dialog_export_messages.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index adda1b70..3e772ac1 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -10,6 +10,16 @@ android:id="@+id/settings" android:title="@string/settings" app:showAsAction="never" /> + + + + Sender doesn\'t support replies Draft Sending… - Export messages - Import messages New conversation @@ -50,6 +48,13 @@ Sender only Enable delivery reports + + Messages + Export messages + Export SMS + Export MMS + Import messages + Why does the app require access to the internet? Sadly it is needed for sending MMS attachments. Not being able to send MMS would be a really huge disadvantage compared to other apps, so we decided to go this way. From b74a511a5e493364029b93ed6efe41345ed18f43 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 10 Sep 2021 00:31:04 +0100 Subject: [PATCH 02/29] Add dialog to import messages --- .../smsmessenger/activities/MainActivity.kt | 65 ++++++++++++++++++- .../dialogs/ImportMessagesDialog.kt | 38 +++++++++++ .../smsmessenger/helpers/Config.kt | 8 +++ .../smsmessenger/helpers/Constants.kt | 2 + .../res/layout/dialog_import_messages.xml | 35 ++++++++++ app/src/main/res/values/strings.xml | 2 + 6 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt create mode 100644 app/src/main/res/layout/dialog_import_messages.xml 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 1368b3ab..1e0fff09 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -8,10 +8,12 @@ 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.view.Menu import android.view.MenuItem +import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.models.FAQItem @@ -19,12 +21,14 @@ import com.simplemobiletools.smsmessenger.BuildConfig import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.adapters.ConversationsAdapter import com.simplemobiletools.smsmessenger.dialogs.ExportMessagesDialog +import com.simplemobiletools.smsmessenger.dialogs.ImportMessagesDialog import com.simplemobiletools.smsmessenger.extensions.* import com.simplemobiletools.smsmessenger.helpers.EXPORT_MIME_TYPE import com.simplemobiletools.smsmessenger.helpers.THREAD_ID import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE import com.simplemobiletools.smsmessenger.models.Conversation import com.simplemobiletools.smsmessenger.models.Events +import java.io.FileOutputStream import java.io.OutputStream import java.util.ArrayList import java.util.Arrays @@ -115,7 +119,7 @@ class MainActivity : SimpleActivity() { R.id.search -> launchSearch() R.id.settings -> launchSettings() R.id.export_messages -> tryToExportMessages() - R.id.import_messages -> launchSettings() + R.id.import_messages -> tryImportMessages() R.id.about -> launchAbout() else -> return super.onOptionsItemSelected(item) } @@ -131,7 +135,7 @@ class MainActivity : SimpleActivity() { 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) @@ -346,7 +350,7 @@ class MainActivity : SimpleActivity() { handlePermission(PERMISSION_WRITE_STORAGE) { if (it) { ExportMessagesDialog(this, config.lastExportPath, false) { file -> - getFileOutputStream(file.toFileDirItem(this), true) { outStream-> + getFileOutputStream(file.toFileDirItem(this), true) { outStream -> exportMessagesTo(outStream) } } @@ -361,6 +365,61 @@ class MainActivity : SimpleActivity() { } } + private fun tryImportMessages() { + if (isQPlus()) { + Intent(Intent.ACTION_GET_CONTENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = EXPORT_MIME_TYPE + startActivityForResult(this, PICK_IMPORT_SOURCE_INTENT) + } + } else { + handlePermission(PERMISSION_READ_STORAGE) { + if (it) { + importEvents() + } + } + } + } + + private fun importEvents() { + FilePickerDialog(this) { + showImportEventsDialog(it) + } + } + + private fun showImportEventsDialog(path: String) { + ImportMessagesDialog(this, path) { refresh -> + if (refresh) { + runOnUiThread { + // refresh + } + } + } + } + + 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/dialogs/ImportMessagesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt new file mode 100644 index 00000000..7cca894d --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -0,0 +1,38 @@ +package com.simplemobiletools.smsmessenger.dialogs + +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import com.simplemobiletools.commons.extensions.setupDialogStuff +import com.simplemobiletools.smsmessenger.R +import com.simplemobiletools.smsmessenger.activities.SimpleActivity +import com.simplemobiletools.smsmessenger.extensions.config +import kotlinx.android.synthetic.main.dialog_import_messages.view.import_mms_checkbox +import kotlinx.android.synthetic.main.dialog_import_messages.view.import_sms_checkbox + +class ImportMessagesDialog( + private val activity: SimpleActivity, + private val path: String, + private val callback: (refresh: Boolean) -> Unit, +) { + + private val config = activity.config + + init { + val view = (activity.layoutInflater.inflate(R.layout.dialog_import_messages, null) as ViewGroup).apply { + import_sms_checkbox.isChecked = config.importSms + import_mms_checkbox.isChecked = config.importMms + } + + AlertDialog.Builder(activity) + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.cancel, null) + .create().apply { + activity.setupDialogStuff(view, this, R.string.import_messages) { + getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + dismiss() + callback.invoke(true) + } + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt index 87af0344..8b590b0e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt @@ -38,4 +38,12 @@ class Config(context: Context) : BaseConfig(context) { get() = prefs.getBoolean(EXPORT_MMS, true) set(exportMms) = prefs.edit().putBoolean(EXPORT_MMS, exportMms).apply() + var importSms: Boolean + get() = prefs.getBoolean(IMPORT_SMS, true) + set(importSms) = prefs.edit().putBoolean(IMPORT_SMS, importSms).apply() + + var importMms: Boolean + get() = prefs.getBoolean(IMPORT_MMS, true) + set(importMms) = prefs.edit().putBoolean(IMPORT_MMS, importMms).apply() + } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt index 04f4b988..7e99610e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt @@ -20,6 +20,8 @@ const val EXPORT_SMS = "export_sms" const val EXPORT_MMS = "export_mms" const val EXPORT_MIME_TYPE = "application/json" const val EXPORT_FILE_EXT = ".json" +const val IMPORT_SMS = "import_sms" +const val IMPORT_MMS = "import_mms" private const val PATH = "com.simplemobiletools.smsmessenger.action." const val MARK_AS_READ = PATH + "mark_as_read" diff --git a/app/src/main/res/layout/dialog_import_messages.xml b/app/src/main/res/layout/dialog_import_messages.xml new file mode 100644 index 00000000..0f057a3b --- /dev/null +++ b/app/src/main/res/layout/dialog_import_messages.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c4f012d1..527171ed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,6 +54,8 @@ Export SMS Export MMS Import messages + Import SMS + Import MMS Why does the app require access to the internet? From 7f32115afe15a8ec0acccef0fa508bb6e5ad9bab Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Sun, 12 Sep 2021 00:43:00 +0100 Subject: [PATCH 03/29] back up messages --- .../smsmessenger/activities/MainActivity.kt | 12 +- .../smsmessenger/extensions/Context.kt | 16 +- .../smsmessenger/extensions/Cursor.kt | 21 +++ .../smsmessenger/extensions/Gson.kt | 99 ++++++++++++ .../smsmessenger/helpers/JsonObjectWriter.kt | 44 ++++++ .../smsmessenger/helpers/MessagesExporter.kt | 84 ++++++++++ .../smsmessenger/helpers/MessagesReader.kt | 143 ++++++++++++++++++ 7 files changed, 417 insertions(+), 2 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Cursor.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Gson.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.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 1e0fff09..32f171f8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -24,6 +24,7 @@ import com.simplemobiletools.smsmessenger.dialogs.ExportMessagesDialog import com.simplemobiletools.smsmessenger.dialogs.ImportMessagesDialog import com.simplemobiletools.smsmessenger.extensions.* import com.simplemobiletools.smsmessenger.helpers.EXPORT_MIME_TYPE +import com.simplemobiletools.smsmessenger.helpers.MessagesExporter import com.simplemobiletools.smsmessenger.helpers.THREAD_ID import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE import com.simplemobiletools.smsmessenger.models.Conversation @@ -45,6 +46,7 @@ class MainActivity : SimpleActivity() { private var storedTextColor = 0 private var storedFontSize = 0 private var bus: EventBus? = null + private val smsExporter by lazy { MessagesExporter(this) } @SuppressLint("InlinedApi") override fun onCreate(savedInstanceState: Bundle?) { @@ -361,7 +363,15 @@ class MainActivity : SimpleActivity() { private fun exportMessagesTo(outputStream: OutputStream?) { ensureBackgroundThread { - + toast(R.string.exporting) + smsExporter.exportMessages(outputStream){ + toast( + when (it) { + MessagesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful + else -> R.string.exporting_failed + } + ) + } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt index fbe15066..10c8889e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt @@ -35,9 +35,9 @@ import com.simplemobiletools.smsmessenger.interfaces.MessagesDao import com.simplemobiletools.smsmessenger.models.* import com.simplemobiletools.smsmessenger.receivers.DirectReplyReceiver import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver -import me.leolin.shortcutbadger.ShortcutBadger import java.util.* import kotlin.collections.ArrayList +import me.leolin.shortcutbadger.ShortcutBadger val Context.config: Config get() = Config.newInstance(applicationContext) @@ -251,6 +251,20 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList< return conversations } +fun Context.getConversationIds(): List { + val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true") + val projection = arrayOf(Threads._ID) + val selection = "${Threads.MESSAGE_COUNT} > ?" + val selectionArgs = arrayOf("0") + val sortOrder = "${Threads.DATE} DESC" + val conversationIds = mutableListOf() + queryCursor(uri, projection, selection, selectionArgs, sortOrder, true) { cursor -> + val id = cursor.getLongValue(Threads._ID) + conversationIds.add(id) + } + return conversationIds +} + // based on https://stackoverflow.com/a/6446831/1967672 @SuppressLint("NewApi") fun Context.getMmsAttachment(id: Long): MessageAttachment { diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Cursor.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Cursor.kt new file mode 100644 index 00000000..8baefa61 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Cursor.kt @@ -0,0 +1,21 @@ +package com.simplemobiletools.smsmessenger.extensions + +import android.database.Cursor +import com.google.gson.JsonNull +import com.google.gson.JsonObject + +fun Cursor. + rowsToJson(): JsonObject { + val obj = JsonObject() + for (i in 0 until columnCount) { + val key = getColumnName(i) + + when (getType(i)) { + Cursor.FIELD_TYPE_INTEGER -> obj.addProperty(key, getLong(i)) + Cursor.FIELD_TYPE_FLOAT -> obj.addProperty(key, getFloat(i)) + Cursor.FIELD_TYPE_STRING -> obj.addProperty(key, getString(i)) + Cursor.FIELD_TYPE_NULL -> obj.add(key, JsonNull.INSTANCE) + } + } + return obj +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Gson.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Gson.kt new file mode 100644 index 00000000..a3e28622 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Gson.kt @@ -0,0 +1,99 @@ +package com.simplemobiletools.smsmessenger.extensions + +import com.google.gson.* +import java.math.BigDecimal +import java.math.BigInteger + + +val JsonElement.optString: String? + get() = safeConversion { asString } + +val JsonElement.optLong: Long? + get() = safeConversion { asLong } + +val JsonElement.optBoolean: Boolean? + get() = safeConversion { asBoolean } + +val JsonElement.optFloat: Float? + get() = safeConversion { asFloat } + +val JsonElement.optDouble: Double? + get() = safeConversion { asDouble } + +val JsonElement.optJsonObject: JsonObject? + get() = safeConversion { asJsonObject } + +val JsonElement.optJsonArray: JsonArray? + get() = safeConversion { asJsonArray } + +val JsonElement.optJsonPrimitive: JsonPrimitive? + get() = safeConversion { asJsonPrimitive } + +val JsonElement.optInt: Int? + get() = safeConversion { asInt } + +val JsonElement.optBigDecimal: BigDecimal? + get() = safeConversion { asBigDecimal } + +val JsonElement.optBigInteger: BigInteger? + get() = safeConversion { asBigInteger } + +val JsonElement.optByte: Byte? + get() = safeConversion { asByte } + +val JsonElement.optShort: Short? + get() = safeConversion { asShort } + +val JsonElement.optJsonNull: JsonNull? + get() = safeConversion { asJsonNull } + +val JsonElement.optCharacter: Char? + get() = safeConversion { asCharacter } + +private fun JsonElement.safeConversion(converter: () -> T?): T? { + + return try { + converter() + } catch (e: Exception) { + null + } +} + +fun JsonObject.optGet(key: String): JsonElement? = get(key) + +fun JsonObject.optGetJsonArray(key: String): JsonArray? = getAsJsonArray(key) + +fun JsonObject.optGetJsonObject(key: String): JsonObject? = getAsJsonObject(key) + +fun JsonObject.optGetJsonPrimitive(key: String): JsonPrimitive? = getAsJsonPrimitive(key) + +fun JsonObject.optString(key: String) = optGet(key)?.asString + +fun JsonObject.optLong(key: String) = optGet(key)?.asLong + +fun JsonObject.optBoolean(key: String) = optGet(key)?.asBoolean + +fun JsonObject.optFloat(key: String) = optGet(key)?.asFloat + +fun JsonObject.optDouble(key: String) = optGet(key)?.asDouble + +fun JsonObject.optJsonObject(key: String) = optGet(key)?.asJsonObject + +fun JsonObject.optJsonArray(key: String) = optGet(key)?.asJsonArray + +fun JsonObject.optJsonPrimitive(key: String) = optGet(key)?.asJsonPrimitive + +fun JsonObject.optInt(key: String) = optGet(key)?.asInt + +fun JsonObject.optBigDecimal(key: String) = optGet(key)?.asBigDecimal + +fun JsonObject.optBigInteger(key: String) = optGet(key)?.asBigInteger + +fun JsonObject.optByte(key: String) = optGet(key)?.asByte + +fun JsonObject.optShort(key: String) = optGet(key)?.asShort + +fun JsonObject.optJsonNull(key: String) = optGet(key)?.asJsonNull + +fun JsonObject.optCharacter(key: String) = optGet(key)?.asCharacter + diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt new file mode 100644 index 00000000..37f6a0d8 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt @@ -0,0 +1,44 @@ +package com.simplemobiletools.smsmessenger.helpers + +import com.google.gson.JsonArray +import com.google.gson.JsonNull +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import com.google.gson.stream.JsonWriter + +class JsonObjectWriter(private val writer: JsonWriter) { + + fun dump(obj: JsonObject) { + writer.beginObject() + for (key in obj.keySet()) { + writer.name(key) + val keyObj = obj.get(key) + dump(keyObj) + } + writer.endObject() + } + + private fun dump(arr: JsonArray) { + writer.beginArray() + for (i in 0 until arr.size()) { + dump(arr.get(i)) + } + writer.endArray() + } + + private fun dump(obj: Any) { + when (obj) { + is JsonNull -> writer.nullValue() + is JsonPrimitive -> { + when{ + obj.isString -> writer.value(obj.asString) + obj.isBoolean -> writer.value(obj.asNumber) + obj.isNumber -> writer.value(obj.asBoolean) + obj.isNumber -> writer.value(obj.asBoolean) + } + } + is JsonArray -> dump(obj) + is JsonObject -> dump(obj) + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt new file mode 100644 index 00000000..0942d95d --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -0,0 +1,84 @@ +package com.simplemobiletools.smsmessenger.helpers + +import android.content.Context +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) + + fun exportMessages( + outputStream: OutputStream?, + callback: (result: ExportResult) -> Unit, + ) { + ensureBackgroundThread { + if (outputStream == null) { + callback.invoke(ExportResult.EXPORT_FAIL) + return@ensureBackgroundThread + } + + /* + * We should have json in this format + * { + * "threadId" : { + * "threadId": "" + * "sms": [{ smses }], + * "mms": [{ mmses }] + * } + * } + * + * */ + val writer = JsonWriter(outputStream.bufferedWriter()) + writer.use { + try { + var written = 0 + writer.beginObject() + val conversationIds = context.getConversationIds() + for(threadId in conversationIds){ + writer.name(threadId.toString()) + + writer.beginObject() + writer.name("threadId") + writer.value(threadId) + if(config.exportSms){ + writer.name("sms") + writer.beginArray() + //write all sms + messageReader.forEachSms(threadId){ + JsonObjectWriter(writer).dump(it) + written++ + } + writer.endArray() + } + + if(config.exportMms){ + writer.name("mms") + writer.beginArray() + //write all mms + messageReader.forEachMms(threadId){ + JsonObjectWriter(writer).dump(it) + written++ + } + + writer.endArray() + } + + writer.endObject() + } + writer.endObject() + callback.invoke(ExportResult.EXPORT_OK) + } catch (e: Exception) { + callback.invoke(ExportResult.EXPORT_FAIL) + } + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt new file mode 100644 index 00000000..d4118234 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -0,0 +1,143 @@ +package com.simplemobiletools.smsmessenger.helpers + +import android.annotation.SuppressLint +import android.content.Context +import android.net.Uri +import android.provider.Telephony +import android.util.Base64 +import android.util.Log +import com.google.android.mms.pdu_alt.PduHeaders +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.simplemobiletools.commons.extensions.getIntValue +import com.simplemobiletools.commons.extensions.getStringValue +import com.simplemobiletools.commons.extensions.queryCursor +import com.simplemobiletools.commons.helpers.isQPlus +import com.simplemobiletools.smsmessenger.extensions.optLong +import com.simplemobiletools.smsmessenger.extensions.optString +import com.simplemobiletools.smsmessenger.extensions.rowsToJson +import java.io.IOException +import java.io.InputStream + +class MessagesReader(private val context: Context) { + companion object { + private const val TAG = "MessagesReader" + private const val MMS_CONTENT = "mms_content" + } + fun forEachSms(threadId: Long, block: (JsonObject) -> Unit) { + forEachThreadMessage(Telephony.Sms.CONTENT_URI, threadId, block) + } + + fun forEachMms(threadId: Long, includeAttachment: Boolean = true, block: (JsonObject) -> Unit) { + forEachThreadMessage(Telephony.Mms.CONTENT_URI, threadId) { obj -> + if (includeAttachment) { + obj.add("parts", getParts(obj.getAsJsonPrimitive("_id").asLong)) + } + obj.add(Telephony.CanonicalAddressesColumns.ADDRESS, getMMSAddresses(obj.getAsJsonPrimitive("_id").asLong)) + block(obj) + } + } + + private fun forEachThreadMessage(contentUri: Uri, threadId: Long, block: (JsonObject) -> Unit) { + val selection = "${Telephony.Sms.THREAD_ID} = ?" + val selectionArgs = arrayOf(threadId.toString()) + context.queryCursor(contentUri, null, selection, selectionArgs) { cursor -> + val json = cursor.rowsToJson() + forceMillisDate(json, "date") + forceMillisDate(json, "date_sent") + block(json) + } + } + + private fun forceMillisDate(message: JsonObject, field: String) { + /* sometimes the sms are in millis and the mms in secs... */ + if (message.get(field).isJsonPrimitive) { + val value = message.get(field).optLong + if (value != null && value != 0L && value < 500000000000L) { // 500000000000 = Tuesday, 5 November 1985 00:53:20 GMT + message.addProperty(field, value * 1000) + } + } + } + + @SuppressLint("NewApi") + private fun getParts(mmsId: Long): JsonArray { + val jsonArray = JsonArray() + val uri = if (isQPlus()) { + Telephony.Mms.Part.CONTENT_URI + } else { + Uri.parse("content://mms/part") + } + + val selection = "${Telephony.Mms.Part.MSG_ID}=$mmsId" + context.queryCursor(uri, emptyArray(), selection) { cursor -> + val part = cursor.rowsToJson() + + val hasTextValue = (part.has(Telephony.Mms.Part.TEXT) && !part.get(Telephony.Mms.Part.TEXT).optString.isNullOrEmpty()) + + when { + hasTextValue -> { + part.addProperty(MMS_CONTENT, "") + } + + part.get(Telephony.Mms.Part.CONTENT_TYPE).optString?.startsWith("text/") == true -> { + part.addProperty(MMS_CONTENT, usePart(part.get(Telephony.Mms.Part._ID).asLong) { stream -> + stream.readBytes().toString(Charsets.UTF_8) + }) + } + else -> { + part.addProperty(MMS_CONTENT, usePart(part.get(Telephony.Mms.Part._ID).asLong) { stream -> + Base64.encodeToString(stream.readBytes(), Base64.DEFAULT) + }) + } + } + jsonArray.add(part) + } + + return jsonArray + } + + @SuppressLint("NewApi") + private fun usePart(partId: Long, block: (InputStream) -> String): String { + val partUri = if (isQPlus()) { + Telephony.Mms.Part.CONTENT_URI.buildUpon().appendPath(partId.toString()).build() + } else { + Uri.parse("content://mms/part/$partId") + } + try { + val stream = context.contentResolver.openInputStream(partUri) + if (stream == null) { + val msg = "failed opening stream for mms part $partUri" + Log.e(TAG, msg) + return "" + } + stream.use { + return block(stream) + } + } catch (e: IOException) { + val msg = "failed to read MMS part on $partUri" + Log.e(TAG, msg, e) + return "" + } + } + + @SuppressLint("NewApi") + private fun getMMSAddresses(messageId: Long): JsonArray { + val jsonArray = JsonArray() + val addressUri = if (isQPlus()) { + Telephony.Mms.Addr.getAddrUriForMessage(messageId.toString()) + } else { + Uri.parse("content://mms/$messageId/addr") + } + + val projection = arrayOf(Telephony.Mms.Addr.ADDRESS, Telephony.Mms.Addr.TYPE) + val selection = "${Telephony.Mms.Addr.MSG_ID}=$messageId" + + context.queryCursor(addressUri, projection, selection) { cursor -> + when (cursor.getIntValue(Telephony.Mms.Addr.TYPE)) { + PduHeaders.FROM, PduHeaders.TO, PduHeaders.CC, PduHeaders.BCC -> jsonArray.add(cursor.getStringValue(Telephony.Mms.Addr.ADDRESS)) + } + } + + return jsonArray + } +} From 9656207135f23b2b6b2aa5a30d52ac14a461592c Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Sun, 12 Sep 2021 18:44:52 +0100 Subject: [PATCH 04/29] Import SMS --- .../smsmessenger/activities/MainActivity.kt | 2 +- .../dialogs/ExportMessagesDialog.kt | 14 ++- .../dialogs/ImportMessagesDialog.kt | 15 +++- .../smsmessenger/extensions/Collections.kt | 21 +++++ .../smsmessenger/extensions/Context.kt | 32 ++++++- .../smsmessenger/extensions/Cursor.kt | 3 +- .../smsmessenger/helpers/Constants.kt | 1 + .../smsmessenger/helpers/JsonObjectWriter.kt | 19 ++--- .../smsmessenger/helpers/MessagesExporter.kt | 4 +- .../smsmessenger/helpers/MessagesImporter.kt | 83 ++++++++++++++++++ .../smsmessenger/helpers/MessagesReader.kt | 1 - .../smsmessenger/helpers/MessagesWriter.kt | 85 +++++++++++++++++++ .../smsmessenger/models/ExportedMessage.kt | 12 +++ .../smsmessenger/models/SmsBackup.kt | 46 ++++++++++ 14 files changed, 310 insertions(+), 28 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.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 32f171f8..daafbfa9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -401,7 +401,7 @@ class MainActivity : SimpleActivity() { ImportMessagesDialog(this, path) { refresh -> if (refresh) { runOnUiThread { - // refresh + } } } 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 5115cfbd..7a52a6ad 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt @@ -4,7 +4,6 @@ import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.activities.SimpleActivity import com.simplemobiletools.smsmessenger.extensions.config @@ -58,14 +57,11 @@ class ExportMessagesDialog( return@setOnClickListener } - ensureBackgroundThread { - config.lastExportPath = file.absolutePath.getParentPath() - config.exportSms = view.export_sms_checkbox.isChecked - config.exportMms = view.export_mms_checkbox.isChecked - - callback(file) - dismiss() - } + config.exportSms = view.export_sms_checkbox.isChecked + config.exportMms = view.export_mms_checkbox.isChecked + config.lastExportPath = file.absolutePath.getParentPath() + callback(file) + 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 7cca894d..7dae30de 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -3,9 +3,12 @@ package com.simplemobiletools.smsmessenger.dialogs import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.extensions.setupDialogStuff +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.extensions.config +import com.simplemobiletools.smsmessenger.helpers.MessagesImporter import kotlinx.android.synthetic.main.dialog_import_messages.view.import_mms_checkbox import kotlinx.android.synthetic.main.dialog_import_messages.view.import_sms_checkbox @@ -29,8 +32,16 @@ class ImportMessagesDialog( .create().apply { activity.setupDialogStuff(view, this, R.string.import_messages) { getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - dismiss() - callback.invoke(true) + activity.toast(R.string.importing) + config.importSms = view.import_sms_checkbox.isChecked + config.importMms = view.import_mms_checkbox.isChecked + ensureBackgroundThread { + MessagesImporter(activity).importMessages(path){ + + } + dismiss() + callback.invoke(true) + } } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Collections.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Collections.kt index bc2acca7..38e3d1b2 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Collections.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Collections.kt @@ -1,5 +1,7 @@ package com.simplemobiletools.smsmessenger.extensions +import android.content.ContentValues + inline fun List.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? { var index = 0 for (item in this) { @@ -9,3 +11,22 @@ inline fun List.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? { } return null } + +fun Map.toContentValues(): ContentValues { + val contentValues = ContentValues() + for (item in entries) { + when (val value = item.value) { + is String -> contentValues.put(item.key, value) + is Byte -> contentValues.put(item.key, value) + is Short -> contentValues.put(item.key, value) + is Int -> contentValues.put(item.key, value) + is Long -> contentValues.put(item.key, value) + is Float -> contentValues.put(item.key, value) + is Double -> contentValues.put(item.key, value) + is Boolean -> contentValues.put(item.key, value) + is ByteArray -> contentValues.put(item.key, value) + } + } + + return contentValues +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt index 10c8889e..e72d6b27 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt @@ -73,7 +73,11 @@ fun Context.getMessages(threadId: Long): ArrayList { val blockedNumbers = getBlockedNumbers() var messages = ArrayList() queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor -> - val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor + val senderNumber = cursor.getStringValue(Sms.ADDRESS) + + if(senderNumber == null){ + return@queryCursor + } val isNumberBlocked = if (blockStatus.containsKey(senderNumber)) { blockStatus[senderNumber]!! @@ -251,6 +255,32 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList< return conversations } +fun Context.getAllMessages(): List { + val uri = Sms.CONTENT_URI + val sortOrder = "${Sms._ID}" + + val messages= mutableListOf() + queryCursor(uri, null, null, null, sortOrder, showErrors = true) { cursor -> + val senderNumber = cursor.getStringValue(Sms.ADDRESS) + val id = cursor.getLongValue(Sms._ID) + val body = cursor.getStringValue(Sms.BODY) + val person = cursor.getStringValue(Sms.PERSON) + val protocol = cursor.getStringValue(Sms.PROTOCOL) + val type = cursor.getIntValue(Sms.TYPE) + val date = (cursor.getLongValue(Sms.DATE) / 1000) + val read = cursor.getIntValue(Sms.READ) + val thread = cursor.getLongValue(Sms.THREAD_ID) + val subscriptionId = cursor.getIntValue(Sms.SUBSCRIPTION_ID) + val status = cursor.getIntValue(Sms.STATUS) + + messages.add(SmsBackup(id, senderNumber, body, "", date, + 0, 0,0 , person, protocol, read, Any(), 0, 0, status, subscriptionId, Any(), thread, type)) + } + + return messages +} + + fun Context.getConversationIds(): List { val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true") val projection = arrayOf(Threads._ID) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Cursor.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Cursor.kt index 8baefa61..fb4044d6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Cursor.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Cursor.kt @@ -4,8 +4,7 @@ import android.database.Cursor import com.google.gson.JsonNull import com.google.gson.JsonObject -fun Cursor. - rowsToJson(): JsonObject { +fun Cursor.rowsToJson(): JsonObject { val obj = JsonObject() for (i in 0 until columnCount) { val key = getColumnName(i) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt index 7e99610e..b7e92d21 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt @@ -22,6 +22,7 @@ const val EXPORT_MIME_TYPE = "application/json" const val EXPORT_FILE_EXT = ".json" const val IMPORT_SMS = "import_sms" const val IMPORT_MMS = "import_mms" +const val MMS_CONTENT = "mms_content" private const val PATH = "com.simplemobiletools.smsmessenger.action." const val MARK_AS_READ = PATH + "mark_as_read" diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt index 37f6a0d8..34b21fda 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt @@ -8,37 +8,36 @@ import com.google.gson.stream.JsonWriter class JsonObjectWriter(private val writer: JsonWriter) { - fun dump(obj: JsonObject) { + fun write(obj: JsonObject) { writer.beginObject() for (key in obj.keySet()) { writer.name(key) val keyObj = obj.get(key) - dump(keyObj) + write(keyObj) } writer.endObject() } - private fun dump(arr: JsonArray) { + private fun write(arr: JsonArray) { writer.beginArray() for (i in 0 until arr.size()) { - dump(arr.get(i)) + write(arr.get(i)) } writer.endArray() } - private fun dump(obj: Any) { + private fun write(obj: Any) { when (obj) { is JsonNull -> writer.nullValue() is JsonPrimitive -> { when{ obj.isString -> writer.value(obj.asString) - obj.isBoolean -> writer.value(obj.asNumber) - obj.isNumber -> writer.value(obj.asBoolean) - obj.isNumber -> writer.value(obj.asBoolean) + obj.isBoolean -> writer.value(obj.asBoolean) + obj.isNumber -> writer.value(obj.asNumber) } } - is JsonArray -> dump(obj) - is JsonObject -> dump(obj) + is JsonArray -> write(obj) + is JsonObject -> write(obj) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt index 0942d95d..1cd4728e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -53,7 +53,7 @@ class MessagesExporter(private val context: Context) { writer.beginArray() //write all sms messageReader.forEachSms(threadId){ - JsonObjectWriter(writer).dump(it) + JsonObjectWriter(writer).write(it) written++ } writer.endArray() @@ -64,7 +64,7 @@ class MessagesExporter(private val context: Context) { writer.beginArray() //write all mms messageReader.forEachMms(threadId){ - JsonObjectWriter(writer).dump(it) + JsonObjectWriter(writer).write(it) written++ } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt new file mode 100644 index 00000000..b8964774 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -0,0 +1,83 @@ +package com.simplemobiletools.smsmessenger.helpers + +import android.content.Context +import android.provider.Telephony +import android.util.Log +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.simplemobiletools.commons.extensions.queryCursor +import com.simplemobiletools.commons.helpers.ensureBackgroundThread +import com.simplemobiletools.smsmessenger.extensions.* +import com.simplemobiletools.smsmessenger.models.ExportedMessage +import java.io.File + +class MessagesImporter(private val context: Context) { + companion object { + private const val TAG = "MessagesImporter" + } + + 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 + + fun importMessages(path: String, callback: (result: ImportResult) -> Unit) { + ensureBackgroundThread { + if (path.isEmpty()) { + callback.invoke(ImportResult.IMPORT_FAIL) + return@ensureBackgroundThread + } + + //read data from path + // parse json + // write data to sql db + val inputStream = if (path.contains("/")) { + File(path).inputStream() + } else { + context.assets.open(path) + } + + inputStream.bufferedReader().use { + try { + val json = it.readText() + Log.d(TAG, "importMessages: json== ${json.length}") + val type = object : TypeToken>() {}.type + val data = gson.fromJson>(json, type) + Log.d(TAG, "importMessages: ${data.size}") + for (message in data.values) { + // add sms + if (config.importSms) { + message.sms.forEach(messageWriter::writeSmsMessage) + } + + // add mms + if (config.importMms) { + message.sms.forEach(messageWriter::writeMmsMessage) + } + +// messageWriter.updateAllSmsThreads() + + val conversations = context.getConversations() + val conversationIds = context.getConversationIds() + Log.w(TAG, "conversations = $conversations") + Log.w(TAG, "conversationIds = $conversationIds") + context.queryCursor(Telephony.Sms.CONTENT_URI) { cursor -> + val json = cursor.rowsToJson() + Log.w(TAG, "messages = $json") + } + + refreshMessages() + + + } + } catch (e: Exception) { + Log.e(TAG, "importMessages: ", e) + callback.invoke(ImportResult.IMPORT_FAIL) + } + } + } + } +} 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 d4118234..33f48c63 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -22,7 +22,6 @@ import java.io.InputStream class MessagesReader(private val context: Context) { companion object { private const val TAG = "MessagesReader" - private const val MMS_CONTENT = "mms_content" } fun forEachSms(threadId: Long, block: (JsonObject) -> Unit) { forEachThreadMessage(Telephony.Sms.CONTENT_URI, threadId, block) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt new file mode 100644 index 00000000..f6ff3f0b --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt @@ -0,0 +1,85 @@ +package com.simplemobiletools.smsmessenger.helpers + +import android.content.ContentValues +import android.content.Context +import android.provider.Telephony +import android.util.Log +import com.simplemobiletools.commons.extensions.queryCursor +import com.simplemobiletools.smsmessenger.extensions.getThreadId +import com.simplemobiletools.smsmessenger.extensions.toContentValues + +class MessagesWriter(private val context: Context) { + companion object { + private const val TAG = "MessagesWriter" + } + + private val contentResolver = context.contentResolver + + fun writeSmsMessage(map: Map) { + Log.w(TAG, "writeSmsMessage: map=$map") + val contentValues = map.toContentValues() + contentValues.remove(Telephony.Sms._ID) + replaceThreadId(contentValues) + Log.d(TAG, "writeSmsMessage: contentValues=$contentValues") + val type = contentValues.getAsInteger(Telephony.Sms.TYPE) + Log.d(TAG, "writeSmsMessage: type=$type") + if ((type == Telephony.Sms.MESSAGE_TYPE_INBOX || type == Telephony.Sms.MESSAGE_TYPE_SENT) && !smsExist(map)) { + Log.d(TAG, "writeSmsMessage: Inserting SMS...") + val uri = Telephony.Sms.CONTENT_URI + Log.d(TAG, "writeSmsMessage: uri=$uri") + contentResolver.insert(Telephony.Sms.CONTENT_URI, contentValues) + } + } + + private fun replaceThreadId(contentValues: ContentValues) { + val address = contentValues.get(Telephony.Sms.ADDRESS) + val threadId = context.getThreadId(address.toString()) + contentValues.put(Telephony.Sms.THREAD_ID, threadId) + } + + fun writeMmsMessage(map: Map) { + + } + + private fun smsExist(sms: Map): Boolean { + val uri = Telephony.Sms.CONTENT_URI + val projection = arrayOf(Telephony.Sms._ID) + val selection = "${Telephony.Sms.DATE} = ? AND ${Telephony.Sms.ADDRESS} = ? AND ${Telephony.Sms.TYPE} = ?" + val selectionArgs = arrayOf(sms[Telephony.Sms.DATE] as String, sms[Telephony.Sms.ADDRESS] as String, sms[Telephony.Sms.TYPE] as String) + + var exists = false + context.queryCursor(uri, projection, selection, selectionArgs) { + exists = it.count > 0 + Log.i(TAG, "smsExist After: $exists") + } + Log.i(TAG, "smsExist: $exists") + + return exists + } + + private fun Map.toSmsContentValues(): ContentValues { + val values = ContentValues() + val address = get(Telephony.Sms.ADDRESS) as String + values.put(Telephony.Sms.ADDRESS, address) + values.put(Telephony.Sms.DATE, get(Telephony.Sms.DATE)) + values.put(Telephony.Sms.DATE_SENT, get(Telephony.Sms.DATE_SENT)) + values.put(Telephony.Sms.BODY, get(Telephony.Sms.BODY) as String) + values.put(Telephony.Sms.TYPE, get(Telephony.Sms.TYPE)) + values.put(Telephony.Sms.PROTOCOL, get(Telephony.Sms.PROTOCOL)) + values.put(Telephony.Sms.SERVICE_CENTER, get(Telephony.Sms.SERVICE_CENTER)) + values.put(Telephony.Sms.STATUS, get(Telephony.Sms.STATUS)) + values.put(Telephony.Sms.READ, get(Telephony.Sms.READ)) + values.put(Telephony.Sms.CREATOR, get(Telephony.Sms.CREATOR)) + values.put(Telephony.Sms.LOCKED, get(Telephony.Sms.LOCKED)) + values.put(Telephony.Sms.SUBSCRIPTION_ID, get(Telephony.Sms.SUBSCRIPTION_ID)) + values.put(Telephony.Sms.THREAD_ID, context.getThreadId(address)) + return values + } + + fun updateAllSmsThreads(){ + // thread dates + states might be wrong, we need to force a full update + // unfortunately there's no direct way to do that in the SDK, but passing a + // negative conversation id to delete should to the trick + contentResolver.delete(Telephony.Sms.Conversations.CONTENT_URI.buildUpon().appendPath("-1").build(), null, null) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt new file mode 100644 index 00000000..fbf52d69 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt @@ -0,0 +1,12 @@ +package com.simplemobiletools.smsmessenger.models + +import com.google.gson.annotations.SerializedName + +data class ExportedMessage( + @SerializedName("threadId") + val threadId: Long, + @SerializedName("sms") + val sms: List>, + @SerializedName("mms") + val mms: List>, +) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt new file mode 100644 index 00000000..2e7fd5e7 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt @@ -0,0 +1,46 @@ +package com.simplemobiletools.smsmessenger.models + + +import android.provider.Telephony +import com.google.gson.annotations.SerializedName + +data class SmsBackup( + @SerializedName(Telephony.Sms._ID) + val id: Long, + @SerializedName(Telephony.Sms.ADDRESS) + val address: String, + @SerializedName(Telephony.Sms.BODY) + val body: String, + @SerializedName(Telephony.Sms.CREATOR) + val creator: String, + @SerializedName(Telephony.Sms.DATE) + val date: Long, + @SerializedName(Telephony.Sms.DATE_SENT) + val dateSent: Int, + @SerializedName(Telephony.Sms.ERROR_CODE) + val errorCode: Int, + @SerializedName(Telephony.Sms.LOCKED) + val locked: Int, + @SerializedName(Telephony.Sms.PERSON) + val person: String, + @SerializedName(Telephony.Sms.PROTOCOL) + val protocol: String, + @SerializedName("read") + val read: Int, + @SerializedName("reply_path_present") + val replyPathPresent: Any, + @SerializedName("seen") + val seen: Int, + @SerializedName("service_center") + val serviceCenter: Any, + @SerializedName("status") + val status: Int, + @SerializedName("sub_id") + val subId: Int, + @SerializedName("subject") + val subject: Any, + @SerializedName("thread_id") + val threadId: Long, + @SerializedName("type") + val type: Int +) From 25a3a2f47b54206f46c621a2a6e6965e3eecfde3 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Mon, 13 Sep 2021 22:27:27 +0100 Subject: [PATCH 05/29] Refactor import/export --- .../dialogs/ImportMessagesDialog.kt | 13 ++++- .../smsmessenger/extensions/Context.kt | 26 --------- .../smsmessenger/extensions/gson/Gson.kt | 9 +++ .../{Gson.kt => gson/JsonElement.kt} | 41 +------------ .../extensions/gson/JsonObject.kt | 45 ++++++++++++++ .../gson/MapDeserializerDoubleAsIntFix.kt | 58 +++++++++++++++++++ .../smsmessenger/helpers/MessagesExporter.kt | 2 +- .../smsmessenger/helpers/MessagesImporter.kt | 16 +---- .../smsmessenger/helpers/MessagesReader.kt | 54 ++++++++--------- .../smsmessenger/helpers/MessagesWriter.kt | 54 ++++++++--------- .../smsmessenger/models/ExportedMessage.kt | 2 +- .../smsmessenger/models/SmsBackup.kt | 46 --------------- 12 files changed, 179 insertions(+), 187 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/Gson.kt rename app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/{Gson.kt => gson/JsonElement.kt} (51%) create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/JsonObject.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/MapDeserializerDoubleAsIntFix.kt delete mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt 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 7dae30de..faef4e3a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -9,6 +9,10 @@ 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_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 kotlinx.android.synthetic.main.dialog_import_messages.view.import_mms_checkbox import kotlinx.android.synthetic.main.dialog_import_messages.view.import_sms_checkbox @@ -36,8 +40,13 @@ class ImportMessagesDialog( config.importSms = view.import_sms_checkbox.isChecked config.importMms = view.import_mms_checkbox.isChecked ensureBackgroundThread { - MessagesImporter(activity).importMessages(path){ - + MessagesImporter(activity).importMessages(path) { result -> + when (result) { + IMPORT_FAIL -> activity.toast(R.string.importing_failed) + IMPORT_OK -> activity.toast(R.string.importing_successful) + IMPORT_PARTIAL -> activity.toast(R.string.importing_some_entries_failed) + IMPORT_NOTHING_NEW -> activity.toast(R.string.no_entries_for_importing) + } } dismiss() callback.invoke(true) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt index e72d6b27..a8dbf570 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt @@ -255,32 +255,6 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList< return conversations } -fun Context.getAllMessages(): List { - val uri = Sms.CONTENT_URI - val sortOrder = "${Sms._ID}" - - val messages= mutableListOf() - queryCursor(uri, null, null, null, sortOrder, showErrors = true) { cursor -> - val senderNumber = cursor.getStringValue(Sms.ADDRESS) - val id = cursor.getLongValue(Sms._ID) - val body = cursor.getStringValue(Sms.BODY) - val person = cursor.getStringValue(Sms.PERSON) - val protocol = cursor.getStringValue(Sms.PROTOCOL) - val type = cursor.getIntValue(Sms.TYPE) - val date = (cursor.getLongValue(Sms.DATE) / 1000) - val read = cursor.getIntValue(Sms.READ) - val thread = cursor.getLongValue(Sms.THREAD_ID) - val subscriptionId = cursor.getIntValue(Sms.SUBSCRIPTION_ID) - val status = cursor.getIntValue(Sms.STATUS) - - messages.add(SmsBackup(id, senderNumber, body, "", date, - 0, 0,0 , person, protocol, read, Any(), 0, 0, status, subscriptionId, Any(), thread, type)) - } - - return messages -} - - fun Context.getConversationIds(): List { val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true") val projection = arrayOf(Threads._ID) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/Gson.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/Gson.kt new file mode 100644 index 00000000..4e1dcf2e --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/Gson.kt @@ -0,0 +1,9 @@ +package com.simplemobiletools.smsmessenger.extensions.gson + +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken + +private val gsonBuilder = GsonBuilder().registerTypeAdapter(object: TypeToken>(){}.type, MapDeserializerDoubleAsIntFix()) +val gson : Gson = gsonBuilder.create() + diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Gson.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/JsonElement.kt similarity index 51% rename from app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Gson.kt rename to app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/JsonElement.kt index a3e28622..876d6353 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Gson.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/JsonElement.kt @@ -1,4 +1,4 @@ -package com.simplemobiletools.smsmessenger.extensions +package com.simplemobiletools.smsmessenger.extensions.gson import com.google.gson.* import java.math.BigDecimal @@ -58,42 +58,3 @@ private fun JsonElement.safeConversion(converter: () -> T?): T? { null } } - -fun JsonObject.optGet(key: String): JsonElement? = get(key) - -fun JsonObject.optGetJsonArray(key: String): JsonArray? = getAsJsonArray(key) - -fun JsonObject.optGetJsonObject(key: String): JsonObject? = getAsJsonObject(key) - -fun JsonObject.optGetJsonPrimitive(key: String): JsonPrimitive? = getAsJsonPrimitive(key) - -fun JsonObject.optString(key: String) = optGet(key)?.asString - -fun JsonObject.optLong(key: String) = optGet(key)?.asLong - -fun JsonObject.optBoolean(key: String) = optGet(key)?.asBoolean - -fun JsonObject.optFloat(key: String) = optGet(key)?.asFloat - -fun JsonObject.optDouble(key: String) = optGet(key)?.asDouble - -fun JsonObject.optJsonObject(key: String) = optGet(key)?.asJsonObject - -fun JsonObject.optJsonArray(key: String) = optGet(key)?.asJsonArray - -fun JsonObject.optJsonPrimitive(key: String) = optGet(key)?.asJsonPrimitive - -fun JsonObject.optInt(key: String) = optGet(key)?.asInt - -fun JsonObject.optBigDecimal(key: String) = optGet(key)?.asBigDecimal - -fun JsonObject.optBigInteger(key: String) = optGet(key)?.asBigInteger - -fun JsonObject.optByte(key: String) = optGet(key)?.asByte - -fun JsonObject.optShort(key: String) = optGet(key)?.asShort - -fun JsonObject.optJsonNull(key: String) = optGet(key)?.asJsonNull - -fun JsonObject.optCharacter(key: String) = optGet(key)?.asCharacter - diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/JsonObject.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/JsonObject.kt new file mode 100644 index 00000000..76e7bc8f --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/JsonObject.kt @@ -0,0 +1,45 @@ +package com.simplemobiletools.smsmessenger.extensions.gson + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive + +fun JsonObject.optGet(key: String): JsonElement? = get(key) + +fun JsonObject.optGetJsonArray(key: String): JsonArray? = getAsJsonArray(key) + +fun JsonObject.optGetJsonObject(key: String): JsonObject? = getAsJsonObject(key) + +fun JsonObject.optGetJsonPrimitive(key: String): JsonPrimitive? = getAsJsonPrimitive(key) + +fun JsonObject.optString(key: String) = optGet(key)?.asString + +fun JsonObject.optLong(key: String) = optGet(key)?.asLong + +fun JsonObject.optBoolean(key: String) = optGet(key)?.asBoolean + +fun JsonObject.optFloat(key: String) = optGet(key)?.asFloat + +fun JsonObject.optDouble(key: String) = optGet(key)?.asDouble + +fun JsonObject.optJsonObject(key: String) = optGet(key)?.asJsonObject + +fun JsonObject.optJsonArray(key: String) = optGet(key)?.asJsonArray + +fun JsonObject.optJsonPrimitive(key: String) = optGet(key)?.asJsonPrimitive + +fun JsonObject.optInt(key: String) = optGet(key)?.asInt + +fun JsonObject.optBigDecimal(key: String) = optGet(key)?.asBigDecimal + +fun JsonObject.optBigInteger(key: String) = optGet(key)?.asBigInteger + +fun JsonObject.optByte(key: String) = optGet(key)?.asByte + +fun JsonObject.optShort(key: String) = optGet(key)?.asShort + +fun JsonObject.optJsonNull(key: String) = optGet(key)?.asJsonNull + +fun JsonObject.optCharacter(key: String) = optGet(key)?.asCharacter + diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/MapDeserializerDoubleAsIntFix.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/MapDeserializerDoubleAsIntFix.kt new file mode 100644 index 00000000..1c08525e --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/gson/MapDeserializerDoubleAsIntFix.kt @@ -0,0 +1,58 @@ +package com.simplemobiletools.smsmessenger.extensions.gson + +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonParseException +import com.google.gson.internal.LinkedTreeMap +import java.lang.reflect.Type +import kotlin.math.ceil + +// https://stackoverflow.com/a/36529534/10552591 +class MapDeserializerDoubleAsIntFix : JsonDeserializer?> { + @Throws(JsonParseException::class) + override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Map? { + return read(json) as Map? + } + + fun read(element: JsonElement): Any? { + when { + element.isJsonArray -> { + val list: MutableList = ArrayList() + val arr = element.asJsonArray + for (anArr in arr) { + list.add(read(anArr)) + } + return list + } + element.isJsonObject -> { + val map: MutableMap = LinkedTreeMap() + val obj = element.asJsonObject + val entitySet = obj.entrySet() + for ((key, value) in entitySet) { + map[key] = read(value) + } + return map + } + element.isJsonPrimitive -> { + val prim = element.asJsonPrimitive + when { + prim.isBoolean -> { + return prim.asBoolean + } + prim.isString -> { + return prim.asString + } + prim.isNumber -> { + val num = prim.asNumber + // here you can handle double int/long values + // and return any type you want + // this solution will transform 3.0 float to long values + return if (ceil(num.toDouble()) == num.toLong().toDouble()) num.toLong() else num.toDouble() + } + } + } + } + return null + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt index 1cd4728e..b81da803 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -25,7 +25,7 @@ class MessagesExporter(private val context: Context) { return@ensureBackgroundThread } - /* + /** * We should have json in this format * { * "threadId" : { 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 b8964774..bd540f5d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -3,11 +3,11 @@ package com.simplemobiletools.smsmessenger.helpers import android.content.Context import android.provider.Telephony import android.util.Log -import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.simplemobiletools.commons.extensions.queryCursor import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.smsmessenger.extensions.* +import com.simplemobiletools.smsmessenger.extensions.gson.gson import com.simplemobiletools.smsmessenger.models.ExportedMessage import java.io.File @@ -20,7 +20,6 @@ class MessagesImporter(private val context: Context) { IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL, IMPORT_NOTHING_NEW } - private val gson = Gson() private val messageWriter = MessagesWriter(context) private val config = context.config @@ -31,9 +30,6 @@ class MessagesImporter(private val context: Context) { return@ensureBackgroundThread } - //read data from path - // parse json - // write data to sql db val inputStream = if (path.contains("/")) { File(path).inputStream() } else { @@ -55,23 +51,15 @@ class MessagesImporter(private val context: Context) { // add mms if (config.importMms) { - message.sms.forEach(messageWriter::writeMmsMessage) + message.mms.forEach(messageWriter::writeMmsMessage) } -// messageWriter.updateAllSmsThreads() - - val conversations = context.getConversations() - val conversationIds = context.getConversationIds() - Log.w(TAG, "conversations = $conversations") - Log.w(TAG, "conversationIds = $conversationIds") context.queryCursor(Telephony.Sms.CONTENT_URI) { cursor -> val json = cursor.rowsToJson() Log.w(TAG, "messages = $json") } refreshMessages() - - } } catch (e: Exception) { Log.e(TAG, "importMessages: ", e) 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 33f48c63..01bd4d55 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -13,8 +13,8 @@ import com.simplemobiletools.commons.extensions.getIntValue import com.simplemobiletools.commons.extensions.getStringValue import com.simplemobiletools.commons.extensions.queryCursor import com.simplemobiletools.commons.helpers.isQPlus -import com.simplemobiletools.smsmessenger.extensions.optLong -import com.simplemobiletools.smsmessenger.extensions.optString +import com.simplemobiletools.smsmessenger.extensions.gson.optLong +import com.simplemobiletools.smsmessenger.extensions.gson.optString import com.simplemobiletools.smsmessenger.extensions.rowsToJson import java.io.IOException import java.io.InputStream @@ -23,16 +23,15 @@ class MessagesReader(private val context: Context) { companion object { private const val TAG = "MessagesReader" } + fun forEachSms(threadId: Long, block: (JsonObject) -> Unit) { forEachThreadMessage(Telephony.Sms.CONTENT_URI, threadId, block) } - fun forEachMms(threadId: Long, includeAttachment: Boolean = true, block: (JsonObject) -> Unit) { + fun forEachMms(threadId: Long, includeNonTextAttachments: Boolean = false, block: (JsonObject) -> Unit) { forEachThreadMessage(Telephony.Mms.CONTENT_URI, threadId) { obj -> - if (includeAttachment) { - obj.add("parts", getParts(obj.getAsJsonPrimitive("_id").asLong)) - } - obj.add(Telephony.CanonicalAddressesColumns.ADDRESS, getMMSAddresses(obj.getAsJsonPrimitive("_id").asLong)) + obj.add(Telephony.CanonicalAddressesColumns.ADDRESS, getMMSAddresses(obj.getAsJsonPrimitive(Telephony.Mms._ID).asLong)) + obj.add("parts", getParts(obj.getAsJsonPrimitive(Telephony.Mms._ID).asLong, includeNonTextAttachments)) block(obj) } } @@ -59,7 +58,7 @@ class MessagesReader(private val context: Context) { } @SuppressLint("NewApi") - private fun getParts(mmsId: Long): JsonArray { + private fun getParts(mmsId: Long, includeNonPlainTextParts: Boolean): JsonArray { val jsonArray = JsonArray() val uri = if (isQPlus()) { Telephony.Mms.Part.CONTENT_URI @@ -67,23 +66,27 @@ class MessagesReader(private val context: Context) { Uri.parse("content://mms/part") } - val selection = "${Telephony.Mms.Part.MSG_ID}=$mmsId" - context.queryCursor(uri, emptyArray(), selection) { cursor -> + Log.d(TAG, "getParts: includeNonPlainTextParts=$includeNonPlainTextParts") + + val selection = "${Telephony.Mms.Part.MSG_ID}= ?" + val selectionArgs = arrayOf(mmsId.toString()) + context.queryCursor(uri, emptyArray(), selection, selectionArgs) { cursor -> val part = cursor.rowsToJson() - - val hasTextValue = (part.has(Telephony.Mms.Part.TEXT) && !part.get(Telephony.Mms.Part.TEXT).optString.isNullOrEmpty()) - when { - hasTextValue -> { - part.addProperty(MMS_CONTENT, "") + (part.has(Telephony.Mms.Part.TEXT) && !part.get(Telephony.Mms.Part.TEXT).optString.isNullOrEmpty()) -> { + Log.d(TAG, "getParts: Add plain text part: $includeNonPlainTextParts") + part.addProperty(MMS_CONTENT, part.get(Telephony.Mms.Part.TEXT).optString) } - part.get(Telephony.Mms.Part.CONTENT_TYPE).optString?.startsWith("text/") == true -> { + includeNonPlainTextParts && part.get(Telephony.Mms.Part.CONTENT_TYPE).optString?.startsWith("text/") == true -> { + Log.d(TAG, "getParts: Adding text mime= ${part.get(Telephony.Mms.Part.CONTENT_TYPE)}") part.addProperty(MMS_CONTENT, usePart(part.get(Telephony.Mms.Part._ID).asLong) { stream -> stream.readBytes().toString(Charsets.UTF_8) }) } - else -> { + + includeNonPlainTextParts -> { + Log.d(TAG, "getParts: Adding: other mime= ${part.get(Telephony.Mms.Part.CONTENT_TYPE)}") part.addProperty(MMS_CONTENT, usePart(part.get(Telephony.Mms.Part._ID).asLong) { stream -> Base64.encodeToString(stream.readBytes(), Base64.DEFAULT) }) @@ -122,18 +125,17 @@ class MessagesReader(private val context: Context) { @SuppressLint("NewApi") private fun getMMSAddresses(messageId: Long): JsonArray { val jsonArray = JsonArray() - val addressUri = if (isQPlus()) { - Telephony.Mms.Addr.getAddrUriForMessage(messageId.toString()) - } else { - Uri.parse("content://mms/$messageId/addr") - } - + val addressUri = Uri.parse("content://mms/$messageId/addr") val projection = arrayOf(Telephony.Mms.Addr.ADDRESS, Telephony.Mms.Addr.TYPE) val selection = "${Telephony.Mms.Addr.MSG_ID}=$messageId" - context.queryCursor(addressUri, projection, selection) { cursor -> - when (cursor.getIntValue(Telephony.Mms.Addr.TYPE)) { - PduHeaders.FROM, PduHeaders.TO, PduHeaders.CC, PduHeaders.BCC -> jsonArray.add(cursor.getStringValue(Telephony.Mms.Addr.ADDRESS)) + when (val type = cursor.getIntValue(Telephony.Mms.Addr.TYPE)) { + PduHeaders.FROM, PduHeaders.TO, PduHeaders.CC, PduHeaders.BCC -> { + val obj = JsonObject() + obj.addProperty(Telephony.Mms.Addr.ADDRESS, cursor.getStringValue(Telephony.Mms.Addr.ADDRESS)) + obj.addProperty(Telephony.Mms.Addr.TYPE, type) + jsonArray.add(obj) + } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt index f6ff3f0b..479474eb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt @@ -15,15 +15,15 @@ class MessagesWriter(private val context: Context) { private val contentResolver = context.contentResolver - fun writeSmsMessage(map: Map) { - Log.w(TAG, "writeSmsMessage: map=$map") - val contentValues = map.toContentValues() + fun writeSmsMessage(smsMap: Map) { + Log.w(TAG, "writeSmsMessage: map=$smsMap") + val contentValues = smsMap.toContentValues() contentValues.remove(Telephony.Sms._ID) - replaceThreadId(contentValues) + replaceSmsThreadId(contentValues) Log.d(TAG, "writeSmsMessage: contentValues=$contentValues") val type = contentValues.getAsInteger(Telephony.Sms.TYPE) Log.d(TAG, "writeSmsMessage: type=$type") - if ((type == Telephony.Sms.MESSAGE_TYPE_INBOX || type == Telephony.Sms.MESSAGE_TYPE_SENT) && !smsExist(map)) { + if ((type == Telephony.Sms.MESSAGE_TYPE_INBOX || type == Telephony.Sms.MESSAGE_TYPE_SENT) && !smsExist(smsMap)) { Log.d(TAG, "writeSmsMessage: Inserting SMS...") val uri = Telephony.Sms.CONTENT_URI Log.d(TAG, "writeSmsMessage: uri=$uri") @@ -31,21 +31,17 @@ class MessagesWriter(private val context: Context) { } } - private fun replaceThreadId(contentValues: ContentValues) { + private fun replaceSmsThreadId(contentValues: ContentValues) { val address = contentValues.get(Telephony.Sms.ADDRESS) val threadId = context.getThreadId(address.toString()) contentValues.put(Telephony.Sms.THREAD_ID, threadId) } - fun writeMmsMessage(map: Map) { - - } - - private fun smsExist(sms: Map): Boolean { + private fun smsExist(smsMap: Map): Boolean { val uri = Telephony.Sms.CONTENT_URI val projection = arrayOf(Telephony.Sms._ID) val selection = "${Telephony.Sms.DATE} = ? AND ${Telephony.Sms.ADDRESS} = ? AND ${Telephony.Sms.TYPE} = ?" - val selectionArgs = arrayOf(sms[Telephony.Sms.DATE] as String, sms[Telephony.Sms.ADDRESS] as String, sms[Telephony.Sms.TYPE] as String) + val selectionArgs = arrayOf(smsMap[Telephony.Sms.DATE].toString(), smsMap[Telephony.Sms.ADDRESS].toString(), smsMap[Telephony.Sms.TYPE].toString()) var exists = false context.queryCursor(uri, projection, selection, selectionArgs) { @@ -57,29 +53,25 @@ class MessagesWriter(private val context: Context) { return exists } - private fun Map.toSmsContentValues(): ContentValues { - val values = ContentValues() - val address = get(Telephony.Sms.ADDRESS) as String - values.put(Telephony.Sms.ADDRESS, address) - values.put(Telephony.Sms.DATE, get(Telephony.Sms.DATE)) - values.put(Telephony.Sms.DATE_SENT, get(Telephony.Sms.DATE_SENT)) - values.put(Telephony.Sms.BODY, get(Telephony.Sms.BODY) as String) - values.put(Telephony.Sms.TYPE, get(Telephony.Sms.TYPE)) - values.put(Telephony.Sms.PROTOCOL, get(Telephony.Sms.PROTOCOL)) - values.put(Telephony.Sms.SERVICE_CENTER, get(Telephony.Sms.SERVICE_CENTER)) - values.put(Telephony.Sms.STATUS, get(Telephony.Sms.STATUS)) - values.put(Telephony.Sms.READ, get(Telephony.Sms.READ)) - values.put(Telephony.Sms.CREATOR, get(Telephony.Sms.CREATOR)) - values.put(Telephony.Sms.LOCKED, get(Telephony.Sms.LOCKED)) - values.put(Telephony.Sms.SUBSCRIPTION_ID, get(Telephony.Sms.SUBSCRIPTION_ID)) - values.put(Telephony.Sms.THREAD_ID, context.getThreadId(address)) - return values - } - fun updateAllSmsThreads(){ // thread dates + states might be wrong, we need to force a full update // unfortunately there's no direct way to do that in the SDK, but passing a // negative conversation id to delete should to the trick contentResolver.delete(Telephony.Sms.Conversations.CONTENT_URI.buildUpon().appendPath("-1").build(), null, null) } + + fun writeMmsMessage(mmsMap: Map) { + val contentValues = mmsMap.toContentValues() + contentValues.remove(Telephony.Mms._ID) + replaceMmsThreadId(contentValues, mmsMap) + Telephony.Mms.MESSAGE_TYPE + + + } + + private fun replaceMmsThreadId(contentValues: ContentValues, mmsMap: Map) { + val addresses = (mmsMap[Telephony.Mms.Addr.ADDRESS] as? List<*>)?.map { it.toString() }?.toHashSet() + val threadId = context.getThreadId(addresses ?: setOf()) + contentValues.put(Telephony.Mms.THREAD_ID, threadId) + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt index fbf52d69..3f7977cd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt @@ -6,7 +6,7 @@ data class ExportedMessage( @SerializedName("threadId") val threadId: Long, @SerializedName("sms") - val sms: List>, + val sms: List>, @SerializedName("mms") val mms: List>, ) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt deleted file mode 100644 index 2e7fd5e7..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.simplemobiletools.smsmessenger.models - - -import android.provider.Telephony -import com.google.gson.annotations.SerializedName - -data class SmsBackup( - @SerializedName(Telephony.Sms._ID) - val id: Long, - @SerializedName(Telephony.Sms.ADDRESS) - val address: String, - @SerializedName(Telephony.Sms.BODY) - val body: String, - @SerializedName(Telephony.Sms.CREATOR) - val creator: String, - @SerializedName(Telephony.Sms.DATE) - val date: Long, - @SerializedName(Telephony.Sms.DATE_SENT) - val dateSent: Int, - @SerializedName(Telephony.Sms.ERROR_CODE) - val errorCode: Int, - @SerializedName(Telephony.Sms.LOCKED) - val locked: Int, - @SerializedName(Telephony.Sms.PERSON) - val person: String, - @SerializedName(Telephony.Sms.PROTOCOL) - val protocol: String, - @SerializedName("read") - val read: Int, - @SerializedName("reply_path_present") - val replyPathPresent: Any, - @SerializedName("seen") - val seen: Int, - @SerializedName("service_center") - val serviceCenter: Any, - @SerializedName("status") - val status: Int, - @SerializedName("sub_id") - val subId: Int, - @SerializedName("subject") - val subject: Any, - @SerializedName("thread_id") - val threadId: Long, - @SerializedName("type") - val type: Int -) From e10a410788259b29131418b099e3657f0d9f9115 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Sat, 18 Sep 2021 16:14:58 +0100 Subject: [PATCH 06/29] feat: restore mms --- .../smsmessenger/extensions/Context.kt | 26 --- .../smsmessenger/helpers/MessagesExporter.kt | 10 +- .../smsmessenger/helpers/MessagesImporter.kt | 16 +- .../smsmessenger/helpers/MessagesReader.kt | 75 ++++--- .../smsmessenger/helpers/MessagesWriter.kt | 210 +++++++++++++++--- .../smsmessenger/models/ExportedMessage.kt | 4 +- .../smsmessenger/models/MmsAddress.kt | 28 +++ .../smsmessenger/models/MmsBackup.kt | 73 ++++++ .../smsmessenger/models/MmsPart.kt | 60 +++++ .../smsmessenger/models/SmsBackup.kt | 78 ++++--- 10 files changed, 438 insertions(+), 142 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt index e72d6b27..a8dbf570 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt @@ -255,32 +255,6 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList< return conversations } -fun Context.getAllMessages(): List { - val uri = Sms.CONTENT_URI - val sortOrder = "${Sms._ID}" - - val messages= mutableListOf() - queryCursor(uri, null, null, null, sortOrder, showErrors = true) { cursor -> - val senderNumber = cursor.getStringValue(Sms.ADDRESS) - val id = cursor.getLongValue(Sms._ID) - val body = cursor.getStringValue(Sms.BODY) - val person = cursor.getStringValue(Sms.PERSON) - val protocol = cursor.getStringValue(Sms.PROTOCOL) - val type = cursor.getIntValue(Sms.TYPE) - val date = (cursor.getLongValue(Sms.DATE) / 1000) - val read = cursor.getIntValue(Sms.READ) - val thread = cursor.getLongValue(Sms.THREAD_ID) - val subscriptionId = cursor.getIntValue(Sms.SUBSCRIPTION_ID) - val status = cursor.getIntValue(Sms.STATUS) - - messages.add(SmsBackup(id, senderNumber, body, "", date, - 0, 0,0 , person, protocol, read, Any(), 0, 0, status, subscriptionId, Any(), thread, type)) - } - - return messages -} - - fun Context.getConversationIds(): List { val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true") val projection = arrayOf(Threads._ID) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt index 1cd4728e..627d9a6a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -42,28 +42,28 @@ class MessagesExporter(private val context: Context) { var written = 0 writer.beginObject() val conversationIds = context.getConversationIds() - for(threadId in conversationIds){ + for (threadId in conversationIds) { writer.name(threadId.toString()) writer.beginObject() writer.name("threadId") writer.value(threadId) - if(config.exportSms){ + if (config.exportSms) { writer.name("sms") writer.beginArray() //write all sms - messageReader.forEachSms(threadId){ + messageReader.forEachSms(threadId) { JsonObjectWriter(writer).write(it) written++ } writer.endArray() } - if(config.exportMms){ + if (config.exportMms) { writer.name("mms") writer.beginArray() //write all mms - messageReader.forEachMms(threadId){ + messageReader.forEachMms(threadId) { JsonObjectWriter(writer).write(it) written++ } 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 b8964774..6d9c218c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -1,6 +1,7 @@ package com.simplemobiletools.smsmessenger.helpers import android.content.Context +import android.net.Uri import android.provider.Telephony import android.util.Log import com.google.gson.Gson @@ -55,7 +56,7 @@ class MessagesImporter(private val context: Context) { // add mms if (config.importMms) { - message.sms.forEach(messageWriter::writeMmsMessage) + message.mms.forEach(messageWriter::writeMmsMessage) } // messageWriter.updateAllSmsThreads() @@ -66,9 +67,20 @@ class MessagesImporter(private val context: Context) { Log.w(TAG, "conversationIds = $conversationIds") context.queryCursor(Telephony.Sms.CONTENT_URI) { cursor -> val json = cursor.rowsToJson() - Log.w(TAG, "messages = $json") + Log.w(TAG, "smses = $json") } + context.queryCursor(Telephony.Mms.CONTENT_URI) { cursor -> + val json = cursor.rowsToJson() + Log.w(TAG, "mmses = $json") + } + + context.queryCursor(Uri.parse("content://mms/part")) { cursor -> + val json = cursor.rowsToJson() + Log.w(TAG, "parts = $json") + } + + refreshMessages() 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 33f48c63..4e602e1c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -6,12 +6,9 @@ import android.net.Uri import android.provider.Telephony import android.util.Base64 import android.util.Log -import com.google.android.mms.pdu_alt.PduHeaders import com.google.gson.JsonArray import com.google.gson.JsonObject -import com.simplemobiletools.commons.extensions.getIntValue -import com.simplemobiletools.commons.extensions.getStringValue -import com.simplemobiletools.commons.extensions.queryCursor +import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.isQPlus import com.simplemobiletools.smsmessenger.extensions.optLong import com.simplemobiletools.smsmessenger.extensions.optString @@ -23,38 +20,35 @@ class MessagesReader(private val context: Context) { companion object { private const val TAG = "MessagesReader" } + fun forEachSms(threadId: Long, block: (JsonObject) -> Unit) { - forEachThreadMessage(Telephony.Sms.CONTENT_URI, threadId, block) - } - - fun forEachMms(threadId: Long, includeAttachment: Boolean = true, block: (JsonObject) -> Unit) { - forEachThreadMessage(Telephony.Mms.CONTENT_URI, threadId) { obj -> - if (includeAttachment) { - obj.add("parts", getParts(obj.getAsJsonPrimitive("_id").asLong)) - } - obj.add(Telephony.CanonicalAddressesColumns.ADDRESS, getMMSAddresses(obj.getAsJsonPrimitive("_id").asLong)) - block(obj) - } - } - - private fun forEachThreadMessage(contentUri: Uri, threadId: Long, block: (JsonObject) -> Unit) { val selection = "${Telephony.Sms.THREAD_ID} = ?" val selectionArgs = arrayOf(threadId.toString()) - context.queryCursor(contentUri, null, selection, selectionArgs) { cursor -> + context.queryCursor(Telephony.Sms.CONTENT_URI, null, selection, selectionArgs) { cursor -> val json = cursor.rowsToJson() - forceMillisDate(json, "date") - forceMillisDate(json, "date_sent") block(json) } } - private fun forceMillisDate(message: JsonObject, field: String) { - /* sometimes the sms are in millis and the mms in secs... */ - if (message.get(field).isJsonPrimitive) { - val value = message.get(field).optLong - if (value != null && value != 0L && value < 500000000000L) { // 500000000000 = Tuesday, 5 November 1985 00:53:20 GMT - message.addProperty(field, value * 1000) - } + // all mms from simple sms are non-text messages + fun forEachMms(threadId: Long, includeTextOnlyAttachment: Boolean = false, block: (JsonObject) -> Unit) { + + val selection = if (includeTextOnlyAttachment) { + "${Telephony.Mms.THREAD_ID} = ? AND ${Telephony.Mms.TEXT_ONLY} = ?" + } else { + "${Telephony.Mms.THREAD_ID} = ?" + } + + val selectionArgs = if (includeTextOnlyAttachment) { + arrayOf(threadId.toString(), "1") + } else { + arrayOf(threadId.toString()) + } + context.queryCursor(Telephony.Mms.CONTENT_URI, null, selection, selectionArgs) { cursor -> + val json = cursor.rowsToJson() + json.add("parts", getParts(json.getAsJsonPrimitive(Telephony.Mms._ID).asLong)) + json.add("addresses", getMMSAddresses(json.getAsJsonPrimitive(Telephony.Mms._ID).asLong)) + block(json) } } @@ -67,8 +61,9 @@ class MessagesReader(private val context: Context) { Uri.parse("content://mms/part") } - val selection = "${Telephony.Mms.Part.MSG_ID}=$mmsId" - context.queryCursor(uri, emptyArray(), selection) { cursor -> + val selection = "${Telephony.Mms.Part.MSG_ID}= ?" + val selectionArgs = arrayOf(mmsId.toString()) + context.queryCursor(uri, null, selection, selectionArgs) { cursor -> val part = cursor.rowsToJson() val hasTextValue = (part.has(Telephony.Mms.Part.TEXT) && !part.get(Telephony.Mms.Part.TEXT).optString.isNullOrEmpty()) @@ -85,7 +80,12 @@ class MessagesReader(private val context: Context) { } else -> { part.addProperty(MMS_CONTENT, usePart(part.get(Telephony.Mms.Part._ID).asLong) { stream -> - Base64.encodeToString(stream.readBytes(), Base64.DEFAULT) + val arr = stream.readBytes() + Log.d(TAG, "getParts: $arr") + Log.d(TAG, "getParts: size = ${arr.size}") + Log.d(TAG, "getParts: US_ASCII-> ${arr.toString(Charsets.US_ASCII)}") + Log.d(TAG, "getParts: UTF_8-> ${arr.toString(Charsets.UTF_8)}") + Base64.encodeToString(arr, Base64.DEFAULT) }) } } @@ -129,12 +129,15 @@ class MessagesReader(private val context: Context) { } val projection = arrayOf(Telephony.Mms.Addr.ADDRESS, Telephony.Mms.Addr.TYPE) - val selection = "${Telephony.Mms.Addr.MSG_ID}=$messageId" + val selection = "${Telephony.Mms.Addr.MSG_ID}= ?" + val selectionArgs = arrayOf(messageId.toString()) - context.queryCursor(addressUri, projection, selection) { cursor -> - when (cursor.getIntValue(Telephony.Mms.Addr.TYPE)) { - PduHeaders.FROM, PduHeaders.TO, PduHeaders.CC, PduHeaders.BCC -> jsonArray.add(cursor.getStringValue(Telephony.Mms.Addr.ADDRESS)) - } + context.queryCursor(addressUri, null, selection, selectionArgs) { cursor -> + val part = cursor.rowsToJson() + jsonArray.add(part) +// when (cursor.getIntValue(Telephony.Mms.Addr.TYPE)) { +// PduHeaders.FROM, PduHeaders.TO, PduHeaders.CC, PduHeaders.BCC -> jsonArray.add(cursor.getStringValue(Telephony.Mms.Addr.ADDRESS)) +// } } return jsonArray diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt index f6ff3f0b..0c17db15 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt @@ -1,29 +1,40 @@ package com.simplemobiletools.smsmessenger.helpers +import android.annotation.SuppressLint import android.content.ContentValues import android.content.Context +import android.net.Uri import android.provider.Telephony +import android.provider.Telephony.Mms +import android.util.Base64 import android.util.Log +import com.google.android.mms.pdu_alt.PduHeaders +import com.klinker.android.send_message.Utils +import com.simplemobiletools.commons.extensions.getLongValue import com.simplemobiletools.commons.extensions.queryCursor +import com.simplemobiletools.commons.helpers.isQPlus import com.simplemobiletools.smsmessenger.extensions.getThreadId -import com.simplemobiletools.smsmessenger.extensions.toContentValues +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 java.nio.charset.Charset class MessagesWriter(private val context: Context) { companion object { + private const val INVALID_ID = -1L private const val TAG = "MessagesWriter" } private val contentResolver = context.contentResolver - fun writeSmsMessage(map: Map) { - Log.w(TAG, "writeSmsMessage: map=$map") - val contentValues = map.toContentValues() - contentValues.remove(Telephony.Sms._ID) - replaceThreadId(contentValues) + fun writeSmsMessage(smsBackup: SmsBackup) { + Log.w(TAG, "writeSmsMessage: smsBackup=$smsBackup") + val contentValues = smsBackup.toContentValues() + replaceSmsThreadId(contentValues) Log.d(TAG, "writeSmsMessage: contentValues=$contentValues") - val type = contentValues.getAsInteger(Telephony.Sms.TYPE) - Log.d(TAG, "writeSmsMessage: type=$type") - if ((type == Telephony.Sms.MESSAGE_TYPE_INBOX || type == Telephony.Sms.MESSAGE_TYPE_SENT) && !smsExist(map)) { + Log.d(TAG, "writeSmsMessage: type=${smsBackup.type}") + if ((smsBackup.type == Telephony.Sms.MESSAGE_TYPE_INBOX || smsBackup.type == Telephony.Sms.MESSAGE_TYPE_SENT) && !smsExist(smsBackup)) { Log.d(TAG, "writeSmsMessage: Inserting SMS...") val uri = Telephony.Sms.CONTENT_URI Log.d(TAG, "writeSmsMessage: uri=$uri") @@ -31,21 +42,17 @@ class MessagesWriter(private val context: Context) { } } - private fun replaceThreadId(contentValues: ContentValues) { + private fun replaceSmsThreadId(contentValues: ContentValues) { val address = contentValues.get(Telephony.Sms.ADDRESS) - val threadId = context.getThreadId(address.toString()) + val threadId = Utils.getOrCreateThreadId(context, address.toString()) contentValues.put(Telephony.Sms.THREAD_ID, threadId) } - fun writeMmsMessage(map: Map) { - - } - - private fun smsExist(sms: Map): Boolean { + private fun smsExist(smsBackup: SmsBackup): Boolean { val uri = Telephony.Sms.CONTENT_URI val projection = arrayOf(Telephony.Sms._ID) val selection = "${Telephony.Sms.DATE} = ? AND ${Telephony.Sms.ADDRESS} = ? AND ${Telephony.Sms.TYPE} = ?" - val selectionArgs = arrayOf(sms[Telephony.Sms.DATE] as String, sms[Telephony.Sms.ADDRESS] as String, sms[Telephony.Sms.TYPE] as String) + val selectionArgs = arrayOf(smsBackup.date.toString(), smsBackup.address, smsBackup.type.toString()) var exists = false context.queryCursor(uri, projection, selection, selectionArgs) { @@ -57,26 +64,159 @@ class MessagesWriter(private val context: Context) { return exists } - private fun Map.toSmsContentValues(): ContentValues { - val values = ContentValues() - val address = get(Telephony.Sms.ADDRESS) as String - values.put(Telephony.Sms.ADDRESS, address) - values.put(Telephony.Sms.DATE, get(Telephony.Sms.DATE)) - values.put(Telephony.Sms.DATE_SENT, get(Telephony.Sms.DATE_SENT)) - values.put(Telephony.Sms.BODY, get(Telephony.Sms.BODY) as String) - values.put(Telephony.Sms.TYPE, get(Telephony.Sms.TYPE)) - values.put(Telephony.Sms.PROTOCOL, get(Telephony.Sms.PROTOCOL)) - values.put(Telephony.Sms.SERVICE_CENTER, get(Telephony.Sms.SERVICE_CENTER)) - values.put(Telephony.Sms.STATUS, get(Telephony.Sms.STATUS)) - values.put(Telephony.Sms.READ, get(Telephony.Sms.READ)) - values.put(Telephony.Sms.CREATOR, get(Telephony.Sms.CREATOR)) - values.put(Telephony.Sms.LOCKED, get(Telephony.Sms.LOCKED)) - values.put(Telephony.Sms.SUBSCRIPTION_ID, get(Telephony.Sms.SUBSCRIPTION_ID)) - values.put(Telephony.Sms.THREAD_ID, context.getThreadId(address)) - return values + fun writeMmsMessage(mmsBackup: MmsBackup) { + // 1. write mms msg, get the msg_id, check if mms exists before writing + // 2. write parts - parts depend on the msg id, check if part exist before writing, store _data in Downloads directory + // 3. write the addresses, address depends on msg id too, check if address exist before writing + Log.w(TAG, "writeMmsMessage: backup=$mmsBackup") + val contentValues = mmsBackup.toContentValues() + val threadId = getMmsThreadId(mmsBackup) + if (threadId != INVALID_ID) { + contentValues.put(Telephony.Mms.THREAD_ID, threadId) + Log.w(TAG, "writeMmsMessage: backup=$mmsBackup") + //write mms + if ((mmsBackup.messageBox == Telephony.Mms.MESSAGE_BOX_INBOX || mmsBackup.messageBox == Telephony.Mms.MESSAGE_BOX_SENT) && !mmsExist(mmsBackup)) { + contentResolver.insert(Telephony.Mms.CONTENT_URI, contentValues) + } + + val messageId = getMmsId(mmsBackup) + if (messageId != INVALID_ID) { + Log.d(TAG, "writing mms addresses") + //write addresses + mmsBackup.addresses.forEach { writeMmsAddress(it, messageId) } + mmsBackup.mmsParts.forEach { writeMmsPart(it, messageId) } + } else { + Log.d(TAG, "failed to write mms message, invalid mms id") + } + } else { + Log.d(TAG, "failed to write mms message, invalid thread id") + } } - fun updateAllSmsThreads(){ + private fun getMmsThreadId(mmsBackup: MmsBackup): Long { + val address = when (mmsBackup.messageBox) { + Mms.MESSAGE_BOX_INBOX -> { + mmsBackup.addresses.firstOrNull { it.type == PduHeaders.FROM }?.address + } + else -> { + mmsBackup.addresses.firstOrNull { it.type == PduHeaders.TO }?.address + } + } + return if (!address.isNullOrEmpty()) { + Utils.getOrCreateThreadId(context, address) + } else { + INVALID_ID + } + } + + private fun getMmsId(mmsBackup: MmsBackup): Long { + val threadId = getMmsThreadId(mmsBackup) + val uri = Telephony.Mms.CONTENT_URI + val projection = arrayOf(Telephony.Mms._ID) + val selection = "${Telephony.Mms.DATE} = ? AND ${Telephony.Mms.DATE_SENT} = ? AND ${Telephony.Mms.THREAD_ID} = ? AND ${Telephony.Mms.MESSAGE_BOX} = ?" + val selectionArgs = arrayOf(mmsBackup.date.toString(), mmsBackup.dateSent.toString(), threadId.toString(), mmsBackup.messageBox.toString()) + + var id = INVALID_ID + context.queryCursor(uri, projection, selection, selectionArgs) { + id = it.getLongValue(Telephony.Mms._ID) + Log.i(TAG, "getMmsId After: $id") + } + Log.i(TAG, "getMmsId: $id") + + return id + } + + private fun mmsExist(mmsBackup: MmsBackup): Boolean { + return getMmsId(mmsBackup) != INVALID_ID + } + + @SuppressLint("NewApi") + private fun mmsAddressExist(mmsAddress: MmsAddress, messageId: Long): Boolean { + val addressUri = if (isQPlus()) { + Telephony.Mms.Addr.getAddrUriForMessage(messageId.toString()) + } else { + Uri.parse("content://mms/$messageId/addr") + } + val projection = arrayOf(Telephony.Mms.Addr._ID) + val selection = "${Telephony.Mms.Addr.TYPE} = ? AND ${Telephony.Mms.Addr.ADDRESS} = ? AND ${Telephony.Mms.Addr.MSG_ID} = ?" + val selectionArgs = arrayOf(mmsAddress.type.toString(), mmsAddress.address.toString(), messageId.toString()) + + var exists = false + context.queryCursor(addressUri, projection, selection, selectionArgs) { + exists = it.count > 0 + Log.i(TAG, "mmsAddressExist After: $exists") + } + Log.i(TAG, "mmsAddressExist: $exists") + + return exists + } + + @SuppressLint("NewApi") + private fun writeMmsAddress(mmsAddress: MmsAddress, messageId: Long) { + if (!mmsAddressExist(mmsAddress, messageId)) { + val addressUri = if (isQPlus()) { + Telephony.Mms.Addr.getAddrUriForMessage(messageId.toString()) + } else { + Uri.parse("content://mms/$messageId/addr") + } + + val contentValues = mmsAddress.toContentValues() + contentValues.put(Telephony.Mms.Addr.MSG_ID, messageId) + contentResolver.insert(addressUri, contentValues) + } else { + Log.w(TAG, "writeMmsAddress: Skip already exists") + } + } + + @SuppressLint("NewApi") + private fun writeMmsPart(mmsPart: MmsPart, messageId: Long) { + Log.d(TAG, "writeMmsPart: Writing part= $mmsPart") + if (!mmsPartExist(mmsPart, messageId)) { + val uri = Uri.parse("content://mms/${messageId}/part") + val contentValues = mmsPart.toContentValues() + contentValues.put(Mms.Part.MSG_ID, messageId) + val partUri = contentResolver.insert(uri, contentValues) + //write data + Log.d(TAG, "writeMmsPart: Inserted part=$partUri") + try { + if (partUri != null) { + if (mmsPart.isNonText()) { + contentResolver.openOutputStream(partUri).use { + val arr = Base64.decode(mmsPart.mmsContent, Base64.DEFAULT) + it!!.write(arr) + Log.d(TAG, "Wrote part data $mmsPart") + } + } else { + Log.w(TAG, "skip writing text content") + } + + } else { + Log.e(TAG, "invalid uri while writing part") + } + } catch (e: Exception) { + Log.e(TAG, "writeMmsPart: uri", e) + } + } + } + + @SuppressLint("NewApi") + private fun mmsPartExist(mmsPart: MmsPart, messageId: Long): Boolean { + val uri = Uri.parse("content://mms/${messageId}/part") + val projection = arrayOf(Telephony.Mms.Part._ID) + val selection = + "${Telephony.Mms.Part.CONTENT_LOCATION} = ? AND ${Telephony.Mms.Part.CT_TYPE} = ? AND ${Mms.Part.MSG_ID} = ? AND ${Telephony.Mms.Part.CONTENT_ID} = ?" + val selectionArgs = arrayOf(mmsPart.contentLocation.toString(), mmsPart.contentType.toString(), messageId.toString(), mmsPart.contentId.toString()) + var exists = false + context.queryCursor(uri, projection, selection, selectionArgs) { + exists = it.count > 0 + Log.i(TAG, "mmsPartExist After: $exists") + } + Log.i(TAG, "mmsPartExist: $exists") + + return exists + } + + fun updateAllSmsThreads() { // thread dates + states might be wrong, we need to force a full update // unfortunately there's no direct way to do that in the SDK, but passing a // negative conversation id to delete should to the trick diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt index fbf52d69..7239d813 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt @@ -6,7 +6,7 @@ data class ExportedMessage( @SerializedName("threadId") val threadId: Long, @SerializedName("sms") - val sms: List>, + val sms: List, @SerializedName("mms") - val mms: List>, + val mms: List, ) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt new file mode 100644 index 00000000..687dabf3 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt @@ -0,0 +1,28 @@ +package com.simplemobiletools.smsmessenger.models + +import android.content.ContentValues +import android.provider.Telephony +import androidx.core.content.contentValuesOf +import com.google.gson.annotations.SerializedName + +data class MmsAddress( + @SerializedName("address") + val address: String, + @SerializedName("msg_id") + val msgId: Int, + @SerializedName("type") + val type: Int, + @SerializedName("charset") + val charset: Int +) { + + fun toContentValues(): ContentValues { + // msgId would be added at the point of insertion + // because it may have changed + return contentValuesOf( + Telephony.Mms.Addr.ADDRESS to address, + Telephony.Mms.Addr.TYPE to type, + Telephony.Mms.Addr.CHARSET to charset, + ) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt new file mode 100644 index 00000000..e1ed2aac --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt @@ -0,0 +1,73 @@ +package com.simplemobiletools.smsmessenger.models + +import android.content.ContentValues +import android.provider.Telephony +import androidx.core.content.contentValuesOf +import com.google.gson.annotations.SerializedName + +data class MmsBackup( + @SerializedName("creator") + val creator: String?, + @SerializedName("ct_t") + val contentType: String?, + @SerializedName("d_rpt") + val deliveryReport: Int, + @SerializedName("date") + val date: Long, + @SerializedName("date_sent") + val dateSent: Long, + @SerializedName("locked") + val locked: Int, + @SerializedName("m_id") + val messageId: String?, + @SerializedName("m_type") + val messageType: Int, + @SerializedName("msg_box") + val messageBox: Int, + @SerializedName("read") + val read: Int, + @SerializedName("rr") + val readReport: Int, + @SerializedName("seen") + val seen: Int, + @SerializedName("text_only") + val textOnly: Int, + @SerializedName("st") + val status: String?, + @SerializedName("sub") + val subject: String?, + @SerializedName("sub_cs") + val subjectCharSet: String?, + @SerializedName("sub_id") + val subscriptionId: Long, + @SerializedName("thread_id") + val threadId: Long, + @SerializedName("tr_id") + val transactionId: String?, + @SerializedName("addresses") + val addresses: List, + @SerializedName("parts") + val mmsParts: List, +) { + + fun toContentValues(): ContentValues { + return contentValuesOf( + Telephony.Mms.TRANSACTION_ID to transactionId, + Telephony.Mms.SUBSCRIPTION_ID to subscriptionId, + Telephony.Mms.SUBJECT to subject, + Telephony.Mms.DATE to date, + Telephony.Mms.DATE_SENT to dateSent, + Telephony.Mms.LOCKED to locked, + Telephony.Mms.READ to read, + Telephony.Mms.STATUS to status, + Telephony.Mms.SUBJECT_CHARSET to subjectCharSet, + Telephony.Mms.SEEN to seen, + Telephony.Mms.MESSAGE_TYPE to messageType, + Telephony.Mms.MESSAGE_BOX to messageBox, + Telephony.Mms.DELIVERY_REPORT to deliveryReport, + Telephony.Mms.READ_REPORT to readReport, + Telephony.Mms.CONTENT_TYPE to contentType, + Telephony.Mms.TEXT_ONLY to textOnly, + ) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt new file mode 100644 index 00000000..b63f4eaf --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt @@ -0,0 +1,60 @@ +package com.simplemobiletools.smsmessenger.models + +import android.content.ContentValues +import android.provider.Telephony +import androidx.core.content.contentValuesOf +import com.google.gson.annotations.SerializedName + +data class MmsPart( + @SerializedName("cd") + val contentDisposition: String?, + @SerializedName("chset") + val charset: String?, + @SerializedName("cid") + val contentId: String?, + @SerializedName("cl") + val contentLocation: String?, + @SerializedName("ct") + val contentType: String, + @SerializedName("ctt_s") + val ctStart: String?, + @SerializedName("ctt_t") + val ctType: String?, + @SerializedName("_data") + val `data`: String?, + @SerializedName("fn") + val filename: String?, + @SerializedName("_id") + val id: Long, + @SerializedName("mid") + val messageId: Long, + @SerializedName("name") + val name: String, + @SerializedName("seq") + val sequenceOrder: Int, + @SerializedName("text") + val text: String?, + @SerializedName("mms_content") + val mmsContent: String?, +) { + + fun toContentValues(): ContentValues { + return contentValuesOf( + Telephony.Mms.Part.CONTENT_DISPOSITION to contentDisposition, + Telephony.Mms.Part.CHARSET to charset, + Telephony.Mms.Part.CONTENT_ID to contentId, + Telephony.Mms.Part.CONTENT_LOCATION to contentLocation, + Telephony.Mms.Part.CONTENT_TYPE to contentType, + Telephony.Mms.Part.CT_START to ctStart, + Telephony.Mms.Part.CT_TYPE to ctType, + Telephony.Mms.Part.FILENAME to filename, + Telephony.Mms.Part.NAME to name, + Telephony.Mms.Part.SEQ to sequenceOrder, + Telephony.Mms.Part.TEXT to text, + ) + } + + fun isNonText(): Boolean { + return !(text != null || contentType.lowercase().startsWith("text") || contentType.lowercase() == "application/smil") + } +} 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 2e7fd5e7..da289a2a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt @@ -1,46 +1,52 @@ package com.simplemobiletools.smsmessenger.models +import android.content.ContentValues import android.provider.Telephony +import androidx.core.content.contentValuesOf import com.google.gson.annotations.SerializedName data class SmsBackup( - @SerializedName(Telephony.Sms._ID) - val id: Long, - @SerializedName(Telephony.Sms.ADDRESS) - val address: String, - @SerializedName(Telephony.Sms.BODY) - val body: String, - @SerializedName(Telephony.Sms.CREATOR) - val creator: String, - @SerializedName(Telephony.Sms.DATE) - val date: Long, - @SerializedName(Telephony.Sms.DATE_SENT) - val dateSent: Int, - @SerializedName(Telephony.Sms.ERROR_CODE) - val errorCode: Int, - @SerializedName(Telephony.Sms.LOCKED) - val locked: Int, - @SerializedName(Telephony.Sms.PERSON) - val person: String, - @SerializedName(Telephony.Sms.PROTOCOL) - val protocol: String, - @SerializedName("read") - val read: Int, - @SerializedName("reply_path_present") - val replyPathPresent: Any, - @SerializedName("seen") - val seen: Int, - @SerializedName("service_center") - val serviceCenter: Any, - @SerializedName("status") - val status: Int, - @SerializedName("sub_id") - val subId: Int, - @SerializedName("subject") - val subject: Any, @SerializedName("thread_id") val threadId: Long, + @SerializedName("sub_id") + val subscriptionId: Long, + @SerializedName("address") + val address: String, + @SerializedName("body") + val body: String, + @SerializedName("date") + val date: Long, + @SerializedName("date_sent") + val dateSent: Long, + @SerializedName("locked") + val locked: Int, + @SerializedName("protocol") + val protocol: String?, + @SerializedName("read") + val read: Int, + @SerializedName("status") + val status: Int, @SerializedName("type") - val type: Int -) + val type: Int, + @SerializedName("service_center") + val serviceCenter: String? +) { + + fun toContentValues(): ContentValues { + return contentValuesOf( + Telephony.Sms.THREAD_ID to threadId, + Telephony.Sms.SUBSCRIPTION_ID to subscriptionId, + Telephony.Sms.ADDRESS to address, + Telephony.Sms.BODY to body, + Telephony.Sms.DATE to date, + Telephony.Sms.DATE_SENT to dateSent, + Telephony.Sms.LOCKED to locked, + Telephony.Sms.PROTOCOL to protocol, + Telephony.Sms.READ to read, + Telephony.Sms.STATUS to status, + Telephony.Sms.TYPE to type, + Telephony.Sms.SERVICE_CENTER to serviceCenter, + ) + } +} From d78776e288a2da685c86822541501c909dc94d97 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Sat, 18 Sep 2021 21:05:06 +0100 Subject: [PATCH 07/29] feat: use models during export, try to update conversation date during import --- .../smsmessenger/extensions/Context.kt | 2 +- .../smsmessenger/helpers/Constants.kt | 1 - .../smsmessenger/helpers/MessagesExporter.kt | 28 +-- .../smsmessenger/helpers/MessagesImporter.kt | 33 ++- .../smsmessenger/helpers/MessagesReader.kt | 191 ++++++++++++------ .../smsmessenger/helpers/MessagesWriter.kt | 83 ++++---- .../smsmessenger/models/ExportedMessage.kt | 2 - .../smsmessenger/models/MmsAddress.kt | 2 - .../smsmessenger/models/MmsBackup.kt | 6 +- .../smsmessenger/models/MmsPart.kt | 12 +- .../smsmessenger/models/SmsBackup.kt | 5 +- 11 files changed, 216 insertions(+), 149 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt index a8dbf570..1e2479d0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt @@ -260,7 +260,7 @@ fun Context.getConversationIds(): List { val projection = arrayOf(Threads._ID) val selection = "${Threads.MESSAGE_COUNT} > ?" val selectionArgs = arrayOf("0") - val sortOrder = "${Threads.DATE} DESC" + val sortOrder = "${Threads.DATE} ASC" val conversationIds = mutableListOf() queryCursor(uri, projection, selection, selectionArgs, sortOrder, true) { cursor -> val id = cursor.getLongValue(Threads._ID) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt index b7e92d21..7e99610e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt @@ -22,7 +22,6 @@ const val EXPORT_MIME_TYPE = "application/json" const val EXPORT_FILE_EXT = ".json" const val IMPORT_SMS = "import_sms" const val IMPORT_MMS = "import_mms" -const val MMS_CONTENT = "mms_content" private const val PATH = "com.simplemobiletools.smsmessenger.action." const val MARK_AS_READ = PATH + "mark_as_read" diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt index 627d9a6a..8251b871 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -1,6 +1,7 @@ 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 @@ -14,6 +15,7 @@ class MessagesExporter(private val context: Context) { private val config = context.config private val messageReader = MessagesReader(context) + private val gson = Gson() fun exportMessages( outputStream: OutputStream?, @@ -27,33 +29,33 @@ class MessagesExporter(private val context: Context) { /* * We should have json in this format - * { - * "threadId" : { - * "threadId": "" + * [ + * { * "sms": [{ smses }], * "mms": [{ mmses }] - * } - * } + * }, + * { + * "sms": [{ smses }], + * "mms": [{ mmses }] + * } + * ] * * */ val writer = JsonWriter(outputStream.bufferedWriter()) writer.use { try { var written = 0 - writer.beginObject() + writer.beginArray() val conversationIds = context.getConversationIds() + val totalMessages = messageReader.getMessagesCount() for (threadId in conversationIds) { - writer.name(threadId.toString()) - writer.beginObject() - writer.name("threadId") - writer.value(threadId) if (config.exportSms) { writer.name("sms") writer.beginArray() //write all sms messageReader.forEachSms(threadId) { - JsonObjectWriter(writer).write(it) + writer.jsonValue(gson.toJson(it)) written++ } writer.endArray() @@ -64,7 +66,7 @@ class MessagesExporter(private val context: Context) { writer.beginArray() //write all mms messageReader.forEachMms(threadId) { - JsonObjectWriter(writer).write(it) + writer.jsonValue(gson.toJson(it)) written++ } @@ -73,7 +75,7 @@ class MessagesExporter(private val context: Context) { writer.endObject() } - 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/helpers/MessagesImporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt index 6d9c218c..b155cdc8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -3,6 +3,7 @@ package com.simplemobiletools.smsmessenger.helpers import android.content.Context import android.net.Uri import android.provider.Telephony +import android.provider.Telephony.* import android.util.Log import com.google.gson.Gson import com.google.gson.reflect.TypeToken @@ -32,9 +33,6 @@ class MessagesImporter(private val context: Context) { return@ensureBackgroundThread } - //read data from path - // parse json - // write data to sql db val inputStream = if (path.contains("/")) { File(path).inputStream() } else { @@ -44,33 +42,32 @@ class MessagesImporter(private val context: Context) { inputStream.bufferedReader().use { try { val json = it.readText() - Log.d(TAG, "importMessages: json== ${json.length}") - val type = object : TypeToken>() {}.type - val data = gson.fromJson>(json, type) - Log.d(TAG, "importMessages: ${data.size}") - for (message in data.values) { + Log.d(TAG, "importMessages: json== $json") + val type = object : TypeToken>() {}.type + val messages = gson.fromJson>(json, type) + Log.d(TAG, "importMessages: ${messages.size}") + for (message in messages) { // add sms if (config.importSms) { message.sms.forEach(messageWriter::writeSmsMessage) } - // add mms if (config.importMms) { message.mms.forEach(messageWriter::writeMmsMessage) } -// messageWriter.updateAllSmsThreads() - val conversations = context.getConversations() - val conversationIds = context.getConversationIds() - Log.w(TAG, "conversations = $conversations") - Log.w(TAG, "conversationIds = $conversationIds") - context.queryCursor(Telephony.Sms.CONTENT_URI) { cursor -> + context.queryCursor(Threads.CONTENT_URI) { cursor -> + val json = cursor.rowsToJson() + Log.w(TAG, "converations = $json") + } + + context.queryCursor(Sms.CONTENT_URI) { cursor -> val json = cursor.rowsToJson() Log.w(TAG, "smses = $json") } - context.queryCursor(Telephony.Mms.CONTENT_URI) { cursor -> + context.queryCursor(Mms.CONTENT_URI) { cursor -> val json = cursor.rowsToJson() Log.w(TAG, "mmses = $json") } @@ -80,10 +77,8 @@ class MessagesImporter(private val context: Context) { Log.w(TAG, "parts = $json") } - refreshMessages() - - + callback.invoke(ImportResult.IMPORT_OK) } } catch (e: Exception) { Log.e(TAG, "importMessages: ", e) 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 4e602e1c..0d02c1be 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -3,16 +3,16 @@ package com.simplemobiletools.smsmessenger.helpers import android.annotation.SuppressLint import android.content.Context import android.net.Uri -import android.provider.Telephony +import android.provider.Telephony.* import android.util.Base64 import android.util.Log -import com.google.gson.JsonArray -import com.google.gson.JsonObject import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.isQPlus -import com.simplemobiletools.smsmessenger.extensions.optLong -import com.simplemobiletools.smsmessenger.extensions.optString import com.simplemobiletools.smsmessenger.extensions.rowsToJson +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 java.io.IOException import java.io.InputStream @@ -21,22 +21,63 @@ class MessagesReader(private val context: Context) { private const val TAG = "MessagesReader" } - fun forEachSms(threadId: Long, block: (JsonObject) -> Unit) { - val selection = "${Telephony.Sms.THREAD_ID} = ?" + fun forEachSms(threadId: Long, block: (SmsBackup) -> Unit) { + 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 + ) + val selection = "${Sms.THREAD_ID} = ?" val selectionArgs = arrayOf(threadId.toString()) - context.queryCursor(Telephony.Sms.CONTENT_URI, null, selection, selectionArgs) { cursor -> - val json = cursor.rowsToJson() - block(json) + 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)) } } // all mms from simple sms are non-text messages - fun forEachMms(threadId: Long, includeTextOnlyAttachment: Boolean = false, block: (JsonObject) -> Unit) { - + fun forEachMms(threadId: Long, includeTextOnlyAttachment: Boolean = false, block: (MmsBackup) -> Unit) { + val projection = arrayOf( + Mms._ID, + Mms.CREATOR, + Mms.CONTENT_TYPE, + Mms.DELIVERY_REPORT, + Mms.DATE, + Mms.DATE_SENT, + Mms.LOCKED, + Mms.MESSAGE_TYPE, + Mms.MESSAGE_BOX, + Mms.READ, + Mms.READ_REPORT, + Mms.SEEN, + Mms.TEXT_ONLY, + Mms.STATUS, + Mms.SUBJECT_CHARSET, + Mms.SUBSCRIPTION_ID, + Mms.TRANSACTION_ID + ) val selection = if (includeTextOnlyAttachment) { - "${Telephony.Mms.THREAD_ID} = ? AND ${Telephony.Mms.TEXT_ONLY} = ?" + "${Mms.THREAD_ID} = ? AND ${Mms.TEXT_ONLY} = ?" } else { - "${Telephony.Mms.THREAD_ID} = ?" + "${Mms.THREAD_ID} = ?" } val selectionArgs = if (includeTextOnlyAttachment) { @@ -44,61 +85,95 @@ class MessagesReader(private val context: Context) { } else { arrayOf(threadId.toString()) } - context.queryCursor(Telephony.Mms.CONTENT_URI, null, selection, selectionArgs) { cursor -> - val json = cursor.rowsToJson() - json.add("parts", getParts(json.getAsJsonPrimitive(Telephony.Mms._ID).asLong)) - json.add("addresses", getMMSAddresses(json.getAsJsonPrimitive(Telephony.Mms._ID).asLong)) - block(json) + 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)) } } @SuppressLint("NewApi") - private fun getParts(mmsId: Long): JsonArray { - val jsonArray = JsonArray() + private fun getParts(mmsId: Long): List { + val parts = mutableListOf() val uri = if (isQPlus()) { - Telephony.Mms.Part.CONTENT_URI + Mms.Part.CONTENT_URI } else { Uri.parse("content://mms/part") } - - val selection = "${Telephony.Mms.Part.MSG_ID}= ?" + val projection = arrayOf( + Mms.Part._ID, + Mms.Part.CONTENT_DISPOSITION, + Mms.Part.CHARSET, + Mms.Part.CONTENT_ID, + Mms.Part.CONTENT_LOCATION, + Mms.Part.CONTENT_TYPE, + Mms.Part.CT_START, + Mms.Part.CT_TYPE, + Mms.Part.FILENAME, + Mms.Part.NAME, + Mms.Part.SEQ, + Mms.Part.TEXT + ) + val selection = "${Mms.Part.MSG_ID}= ?" val selectionArgs = arrayOf(mmsId.toString()) - context.queryCursor(uri, null, selection, selectionArgs) { cursor -> - val part = cursor.rowsToJson() - - val hasTextValue = (part.has(Telephony.Mms.Part.TEXT) && !part.get(Telephony.Mms.Part.TEXT).optString.isNullOrEmpty()) - - when { - hasTextValue -> { - part.addProperty(MMS_CONTENT, "") - } - - part.get(Telephony.Mms.Part.CONTENT_TYPE).optString?.startsWith("text/") == true -> { - part.addProperty(MMS_CONTENT, usePart(part.get(Telephony.Mms.Part._ID).asLong) { stream -> + context.queryCursor(uri, projection, selection, selectionArgs) { cursor -> + val partId = cursor.getLongValue(Mms.Part._ID) + val contentDisposition = cursor.getStringValueOrNull(Mms.Part.CONTENT_DISPOSITION) + val charset = cursor.getStringValueOrNull(Mms.Part.CHARSET) + val contentId = cursor.getStringValueOrNull(Mms.Part.CONTENT_ID) + val contentLocation = cursor.getStringValueOrNull(Mms.Part.CONTENT_LOCATION) + val contentType = cursor.getStringValue(Mms.Part.CONTENT_TYPE) + val ctStart = cursor.getStringValueOrNull(Mms.Part.CT_START) + val ctType = cursor.getStringValueOrNull(Mms.Part.CT_TYPE) + val filename = cursor.getStringValueOrNull(Mms.Part.FILENAME) + val name = cursor.getStringValueOrNull(Mms.Part.NAME) + val sequenceOrder = cursor.getIntValue(Mms.Part.SEQ) + val text = cursor.getStringValueOrNull(Mms.Part.TEXT) + val data = when { + contentType.startsWith("text/") -> { + usePart(partId) { stream -> stream.readBytes().toString(Charsets.UTF_8) - }) + } } else -> { - part.addProperty(MMS_CONTENT, usePart(part.get(Telephony.Mms.Part._ID).asLong) { stream -> + usePart(partId) { stream -> val arr = stream.readBytes() Log.d(TAG, "getParts: $arr") Log.d(TAG, "getParts: size = ${arr.size}") Log.d(TAG, "getParts: US_ASCII-> ${arr.toString(Charsets.US_ASCII)}") Log.d(TAG, "getParts: UTF_8-> ${arr.toString(Charsets.UTF_8)}") Base64.encodeToString(arr, Base64.DEFAULT) - }) + } } } - jsonArray.add(part) + parts.add(MmsPart(contentDisposition, charset, contentId, contentLocation, contentType, ctStart, ctType, filename, name, sequenceOrder, text, data)) } - - return jsonArray + return parts } @SuppressLint("NewApi") private fun usePart(partId: Long, block: (InputStream) -> String): String { val partUri = if (isQPlus()) { - Telephony.Mms.Part.CONTENT_URI.buildUpon().appendPath(partId.toString()).build() + Mms.Part.CONTENT_URI.buildUpon().appendPath(partId.toString()).build() } else { Uri.parse("content://mms/part/$partId") } @@ -120,26 +195,28 @@ class MessagesReader(private val context: Context) { } @SuppressLint("NewApi") - private fun getMMSAddresses(messageId: Long): JsonArray { - val jsonArray = JsonArray() - val addressUri = if (isQPlus()) { - Telephony.Mms.Addr.getAddrUriForMessage(messageId.toString()) + private fun getMMSAddresses(messageId: Long): List { + val addresses = mutableListOf() + val uri = if (isQPlus()) { + Mms.Addr.getAddrUriForMessage(messageId.toString()) } else { Uri.parse("content://mms/$messageId/addr") } - - val projection = arrayOf(Telephony.Mms.Addr.ADDRESS, Telephony.Mms.Addr.TYPE) - val selection = "${Telephony.Mms.Addr.MSG_ID}= ?" + val projection = arrayOf(Mms.Addr.ADDRESS, Mms.Addr.TYPE, Mms.Addr.CHARSET) + val selection = "${Mms.Addr.MSG_ID}= ?" val selectionArgs = arrayOf(messageId.toString()) - context.queryCursor(addressUri, null, selection, selectionArgs) { cursor -> - val part = cursor.rowsToJson() - jsonArray.add(part) -// when (cursor.getIntValue(Telephony.Mms.Addr.TYPE)) { -// PduHeaders.FROM, PduHeaders.TO, PduHeaders.CC, PduHeaders.BCC -> jsonArray.add(cursor.getStringValue(Telephony.Mms.Addr.ADDRESS)) -// } + context.queryCursor(uri, projection, selection, selectionArgs) { cursor -> + val address = cursor.getStringValue(Mms.Addr.ADDRESS) + val type = cursor.getIntValue(Mms.Addr.TYPE) + val charset = cursor.getIntValue(Mms.Addr.CHARSET) + addresses.add(MmsAddress(address, type, charset)) } - return jsonArray + return addresses + } + + fun getMessagesCount(): Long { + return 0 } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt index 0c17db15..10c9d5a9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt @@ -5,20 +5,19 @@ import android.content.ContentValues import android.content.Context import android.net.Uri import android.provider.Telephony -import android.provider.Telephony.Mms +import android.provider.Telephony.* import android.util.Base64 import android.util.Log +import androidx.core.content.contentValuesOf import com.google.android.mms.pdu_alt.PduHeaders import com.klinker.android.send_message.Utils import com.simplemobiletools.commons.extensions.getLongValue import com.simplemobiletools.commons.extensions.queryCursor import com.simplemobiletools.commons.helpers.isQPlus -import com.simplemobiletools.smsmessenger.extensions.getThreadId 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 java.nio.charset.Charset class MessagesWriter(private val context: Context) { companion object { @@ -31,66 +30,79 @@ class MessagesWriter(private val context: Context) { fun writeSmsMessage(smsBackup: SmsBackup) { Log.w(TAG, "writeSmsMessage: smsBackup=$smsBackup") val contentValues = smsBackup.toContentValues() - replaceSmsThreadId(contentValues) + val threadId = Utils.getOrCreateThreadId(context, smsBackup.address) + contentValues.put(Sms.THREAD_ID, threadId) Log.d(TAG, "writeSmsMessage: contentValues=$contentValues") Log.d(TAG, "writeSmsMessage: type=${smsBackup.type}") - if ((smsBackup.type == Telephony.Sms.MESSAGE_TYPE_INBOX || smsBackup.type == Telephony.Sms.MESSAGE_TYPE_SENT) && !smsExist(smsBackup)) { + if (!smsExist(smsBackup)) { Log.d(TAG, "writeSmsMessage: Inserting SMS...") - val uri = Telephony.Sms.CONTENT_URI + val uri = Sms.CONTENT_URI Log.d(TAG, "writeSmsMessage: uri=$uri") - contentResolver.insert(Telephony.Sms.CONTENT_URI, contentValues) + contentResolver.insert(Sms.CONTENT_URI, contentValues) + } else { + Log.w(TAG, "SMS already exists") } + // update conversation date + updateThreadDate(threadId, smsBackup.date) } - private fun replaceSmsThreadId(contentValues: ContentValues) { - val address = contentValues.get(Telephony.Sms.ADDRESS) - val threadId = Utils.getOrCreateThreadId(context, address.toString()) - contentValues.put(Telephony.Sms.THREAD_ID, threadId) + private fun updateThreadDate( + threadId: Long, + date: Long, + ) { + Log.d(TAG, "updateThreadDate: threadId=$threadId -- date=$date") + val selection = "${Threads._ID} = ?" + val selectionArgs = arrayOf(threadId.toString()) + val threadValues = contentValuesOf(Threads.DATE to date) + Log.d(TAG, "threadValues=$threadValues") + val result = contentResolver.update(Threads.CONTENT_URI.buildUpon().appendPath(threadId.toString()).build(), threadValues, null, null) + Log.d(TAG, "updateThreadDate: id=$result") } private fun smsExist(smsBackup: SmsBackup): Boolean { - val uri = Telephony.Sms.CONTENT_URI - val projection = arrayOf(Telephony.Sms._ID) - val selection = "${Telephony.Sms.DATE} = ? AND ${Telephony.Sms.ADDRESS} = ? AND ${Telephony.Sms.TYPE} = ?" + val uri = Sms.CONTENT_URI + val projection = arrayOf(Sms._ID) + val selection = "${Sms.DATE} = ? AND ${Sms.ADDRESS} = ? AND ${Sms.TYPE} = ?" val selectionArgs = arrayOf(smsBackup.date.toString(), smsBackup.address, smsBackup.type.toString()) - var exists = false context.queryCursor(uri, projection, selection, selectionArgs) { exists = it.count > 0 Log.i(TAG, "smsExist After: $exists") } Log.i(TAG, "smsExist: $exists") - return exists } fun writeMmsMessage(mmsBackup: MmsBackup) { // 1. write mms msg, get the msg_id, check if mms exists before writing - // 2. write parts - parts depend on the msg id, check if part exist before writing, store _data in Downloads directory + // 2. write parts - parts depend on the msg id, check if part exist before writing, write data if it is a non-text part // 3. write the addresses, address depends on msg id too, check if address exist before writing Log.w(TAG, "writeMmsMessage: backup=$mmsBackup") val contentValues = mmsBackup.toContentValues() val threadId = getMmsThreadId(mmsBackup) if (threadId != INVALID_ID) { - contentValues.put(Telephony.Mms.THREAD_ID, threadId) + contentValues.put(Mms.THREAD_ID, threadId) Log.w(TAG, "writeMmsMessage: backup=$mmsBackup") //write mms - if ((mmsBackup.messageBox == Telephony.Mms.MESSAGE_BOX_INBOX || mmsBackup.messageBox == Telephony.Mms.MESSAGE_BOX_SENT) && !mmsExist(mmsBackup)) { - contentResolver.insert(Telephony.Mms.CONTENT_URI, contentValues) + if (!mmsExist(mmsBackup)) { + contentResolver.insert(Mms.CONTENT_URI, contentValues) + updateThreadDate(threadId, mmsBackup.date) + } else { + Log.w(TAG, "mms already exists") } - val messageId = getMmsId(mmsBackup) if (messageId != INVALID_ID) { Log.d(TAG, "writing mms addresses") //write addresses mmsBackup.addresses.forEach { writeMmsAddress(it, messageId) } - mmsBackup.mmsParts.forEach { writeMmsPart(it, messageId) } + mmsBackup.parts.forEach { writeMmsPart(it, messageId) } } else { Log.d(TAG, "failed to write mms message, invalid mms id") } } else { Log.d(TAG, "failed to write mms message, invalid thread id") } + } private fun getMmsThreadId(mmsBackup: MmsBackup): Long { @@ -111,14 +123,14 @@ class MessagesWriter(private val context: Context) { private fun getMmsId(mmsBackup: MmsBackup): Long { val threadId = getMmsThreadId(mmsBackup) - val uri = Telephony.Mms.CONTENT_URI - val projection = arrayOf(Telephony.Mms._ID) - val selection = "${Telephony.Mms.DATE} = ? AND ${Telephony.Mms.DATE_SENT} = ? AND ${Telephony.Mms.THREAD_ID} = ? AND ${Telephony.Mms.MESSAGE_BOX} = ?" + val uri = Mms.CONTENT_URI + val projection = arrayOf(Mms._ID) + val selection = "${Mms.DATE} = ? AND ${Mms.DATE_SENT} = ? AND ${Mms.THREAD_ID} = ? AND ${Mms.MESSAGE_BOX} = ?" val selectionArgs = arrayOf(mmsBackup.date.toString(), mmsBackup.dateSent.toString(), threadId.toString(), mmsBackup.messageBox.toString()) var id = INVALID_ID context.queryCursor(uri, projection, selection, selectionArgs) { - id = it.getLongValue(Telephony.Mms._ID) + id = it.getLongValue(Mms._ID) Log.i(TAG, "getMmsId After: $id") } Log.i(TAG, "getMmsId: $id") @@ -133,12 +145,12 @@ class MessagesWriter(private val context: Context) { @SuppressLint("NewApi") private fun mmsAddressExist(mmsAddress: MmsAddress, messageId: Long): Boolean { val addressUri = if (isQPlus()) { - Telephony.Mms.Addr.getAddrUriForMessage(messageId.toString()) + Mms.Addr.getAddrUriForMessage(messageId.toString()) } else { Uri.parse("content://mms/$messageId/addr") } - val projection = arrayOf(Telephony.Mms.Addr._ID) - val selection = "${Telephony.Mms.Addr.TYPE} = ? AND ${Telephony.Mms.Addr.ADDRESS} = ? AND ${Telephony.Mms.Addr.MSG_ID} = ?" + val projection = arrayOf(Mms.Addr._ID) + val selection = "${Mms.Addr.TYPE} = ? AND ${Mms.Addr.ADDRESS} = ? AND ${Mms.Addr.MSG_ID} = ?" val selectionArgs = arrayOf(mmsAddress.type.toString(), mmsAddress.address.toString(), messageId.toString()) var exists = false @@ -155,13 +167,13 @@ class MessagesWriter(private val context: Context) { private fun writeMmsAddress(mmsAddress: MmsAddress, messageId: Long) { if (!mmsAddressExist(mmsAddress, messageId)) { val addressUri = if (isQPlus()) { - Telephony.Mms.Addr.getAddrUriForMessage(messageId.toString()) + Mms.Addr.getAddrUriForMessage(messageId.toString()) } else { Uri.parse("content://mms/$messageId/addr") } val contentValues = mmsAddress.toContentValues() - contentValues.put(Telephony.Mms.Addr.MSG_ID, messageId) + contentValues.put(Mms.Addr.MSG_ID, messageId) contentResolver.insert(addressUri, contentValues) } else { Log.w(TAG, "writeMmsAddress: Skip already exists") @@ -182,14 +194,13 @@ class MessagesWriter(private val context: Context) { if (partUri != null) { if (mmsPart.isNonText()) { contentResolver.openOutputStream(partUri).use { - val arr = Base64.decode(mmsPart.mmsContent, Base64.DEFAULT) + val arr = Base64.decode(mmsPart.data, Base64.DEFAULT) it!!.write(arr) Log.d(TAG, "Wrote part data $mmsPart") } } else { Log.w(TAG, "skip writing text content") } - } else { Log.e(TAG, "invalid uri while writing part") } @@ -202,9 +213,9 @@ class MessagesWriter(private val context: Context) { @SuppressLint("NewApi") private fun mmsPartExist(mmsPart: MmsPart, messageId: Long): Boolean { val uri = Uri.parse("content://mms/${messageId}/part") - val projection = arrayOf(Telephony.Mms.Part._ID) + val projection = arrayOf(Mms.Part._ID) val selection = - "${Telephony.Mms.Part.CONTENT_LOCATION} = ? AND ${Telephony.Mms.Part.CT_TYPE} = ? AND ${Mms.Part.MSG_ID} = ? AND ${Telephony.Mms.Part.CONTENT_ID} = ?" + "${Mms.Part.CONTENT_LOCATION} = ? AND ${Mms.Part.CT_TYPE} = ? AND ${Mms.Part.MSG_ID} = ? AND ${Mms.Part.CONTENT_ID} = ?" val selectionArgs = arrayOf(mmsPart.contentLocation.toString(), mmsPart.contentType.toString(), messageId.toString(), mmsPart.contentId.toString()) var exists = false context.queryCursor(uri, projection, selection, selectionArgs) { @@ -220,6 +231,6 @@ class MessagesWriter(private val context: Context) { // thread dates + states might be wrong, we need to force a full update // unfortunately there's no direct way to do that in the SDK, but passing a // negative conversation id to delete should to the trick - contentResolver.delete(Telephony.Sms.Conversations.CONTENT_URI.buildUpon().appendPath("-1").build(), null, null) + contentResolver.delete(Sms.Conversations.CONTENT_URI.buildUpon().appendPath("-1").build(), null, null) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt index 7239d813..35a5fddc 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt @@ -3,8 +3,6 @@ package com.simplemobiletools.smsmessenger.models import com.google.gson.annotations.SerializedName data class ExportedMessage( - @SerializedName("threadId") - val threadId: Long, @SerializedName("sms") val sms: List, @SerializedName("mms") 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 687dabf3..07148905 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsAddress.kt @@ -8,8 +8,6 @@ import com.google.gson.annotations.SerializedName data class MmsAddress( @SerializedName("address") val address: String, - @SerializedName("msg_id") - val msgId: Int, @SerializedName("type") val type: Int, @SerializedName("charset") 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 e1ed2aac..e8957d93 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsBackup.kt @@ -18,8 +18,6 @@ data class MmsBackup( val dateSent: Long, @SerializedName("locked") val locked: Int, - @SerializedName("m_id") - val messageId: String?, @SerializedName("m_type") val messageType: Int, @SerializedName("msg_box") @@ -40,14 +38,12 @@ data class MmsBackup( val subjectCharSet: String?, @SerializedName("sub_id") val subscriptionId: Long, - @SerializedName("thread_id") - val threadId: Long, @SerializedName("tr_id") val transactionId: String?, @SerializedName("addresses") val addresses: List, @SerializedName("parts") - val mmsParts: List, + val parts: List, ) { fun toContentValues(): ContentValues { 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 b63f4eaf..ac5d53d1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/MmsPart.kt @@ -20,22 +20,16 @@ data class MmsPart( val ctStart: String?, @SerializedName("ctt_t") val ctType: String?, - @SerializedName("_data") - val `data`: String?, @SerializedName("fn") val filename: String?, - @SerializedName("_id") - val id: Long, - @SerializedName("mid") - val messageId: Long, @SerializedName("name") - val name: String, + val name: String?, @SerializedName("seq") val sequenceOrder: Int, @SerializedName("text") val text: String?, - @SerializedName("mms_content") - val mmsContent: String?, + @SerializedName("data") + val data: String?, ) { fun toContentValues(): ContentValues { 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 da289a2a..a6daa883 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/SmsBackup.kt @@ -7,14 +7,12 @@ import androidx.core.content.contentValuesOf import com.google.gson.annotations.SerializedName data class SmsBackup( - @SerializedName("thread_id") - val threadId: Long, @SerializedName("sub_id") val subscriptionId: Long, @SerializedName("address") val address: String, @SerializedName("body") - val body: String, + val body: String?, @SerializedName("date") val date: Long, @SerializedName("date_sent") @@ -35,7 +33,6 @@ data class SmsBackup( fun toContentValues(): ContentValues { return contentValuesOf( - Telephony.Sms.THREAD_ID to threadId, Telephony.Sms.SUBSCRIPTION_ID to subscriptionId, Telephony.Sms.ADDRESS to address, Telephony.Sms.BODY to body, From 24d9988ec1debb64eaa46309e200cb88bb391aa4 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Sat, 18 Sep 2021 21:54:44 +0100 Subject: [PATCH 08/29] Add progress callbacks to importer and exporter, not used yet --- .../smsmessenger/activities/MainActivity.kt | 6 +- .../dialogs/ImportMessagesDialog.kt | 20 +++++- .../smsmessenger/helpers/MessagesExporter.kt | 7 +-- .../smsmessenger/helpers/MessagesImporter.kt | 62 ++++++++++++------- .../smsmessenger/helpers/MessagesReader.kt | 37 ++++++++++- .../smsmessenger/helpers/MessagesWriter.kt | 7 +-- 6 files changed, 101 insertions(+), 38 deletions(-) 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 daafbfa9..3adb9472 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -11,6 +11,7 @@ import android.graphics.drawable.LayerDrawable import android.net.Uri import android.os.Bundle import android.provider.Telephony +import android.util.Log import android.view.Menu import android.view.MenuItem import com.simplemobiletools.commons.dialogs.FilePickerDialog @@ -361,10 +362,13 @@ class MainActivity : SimpleActivity() { } } + private val TAG = "MainActivity" private fun exportMessagesTo(outputStream: OutputStream?) { ensureBackgroundThread { toast(R.string.exporting) - smsExporter.exportMessages(outputStream){ + smsExporter.exportMessages(outputStream, { total, current -> + Log.d(TAG, "PERCENTAGE: ${current.toDouble() * 100 / total.toDouble()}%") + }) { toast( when (it) { MessagesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful 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 7dae30de..36cf521c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -1,5 +1,6 @@ package com.simplemobiletools.smsmessenger.dialogs +import android.util.Log import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.extensions.setupDialogStuff @@ -9,6 +10,7 @@ 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 kotlinx.android.synthetic.main.dialog_import_messages.view.import_mms_checkbox import kotlinx.android.synthetic.main.dialog_import_messages.view.import_sms_checkbox @@ -18,6 +20,7 @@ class ImportMessagesDialog( private val callback: (refresh: Boolean) -> Unit, ) { + private val TAG = "ImportMessagesDialog" private val config = activity.config init { @@ -36,8 +39,10 @@ class ImportMessagesDialog( config.importSms = view.import_sms_checkbox.isChecked config.importMms = view.import_mms_checkbox.isChecked ensureBackgroundThread { - MessagesImporter(activity).importMessages(path){ - + MessagesImporter(activity).importMessages(path, onProgress = { total: Int, current: Int -> + Log.d(TAG, "PERCENTAGE: ${current.toDouble() * 100 / total.toDouble()}%") + }) { + handleParseResult(it) } dismiss() callback.invoke(true) @@ -46,4 +51,15 @@ class ImportMessagesDialog( } } } + + private fun handleParseResult(result: MessagesImporter.ImportResult) { + activity.toast( + when (result) { + IMPORT_OK -> R.string.importing_successful + IMPORT_PARTIAL -> R.string.importing_some_entries_failed + else -> R.string.no_items_found + } + ) + callback(result != IMPORT_FAIL) + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt index 8251b871..609d837f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -17,10 +17,7 @@ class MessagesExporter(private val context: Context) { private val messageReader = MessagesReader(context) private val gson = Gson() - fun exportMessages( - outputStream: OutputStream?, - callback: (result: ExportResult) -> Unit, - ) { + fun exportMessages(outputStream: OutputStream?, onProgress: (total: Int, current: Int) -> Unit = { _, _ -> }, callback: (result: ExportResult) -> Unit) { ensureBackgroundThread { if (outputStream == null) { callback.invoke(ExportResult.EXPORT_FAIL) @@ -57,6 +54,7 @@ class MessagesExporter(private val context: Context) { messageReader.forEachSms(threadId) { writer.jsonValue(gson.toJson(it)) written++ + onProgress.invoke(totalMessages, written) } writer.endArray() } @@ -68,6 +66,7 @@ class MessagesExporter(private val context: Context) { messageReader.forEachMms(threadId) { writer.jsonValue(gson.toJson(it)) written++ + onProgress.invoke(totalMessages, written) } writer.endArray() 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 b155cdc8..7d10c598 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -2,14 +2,15 @@ package com.simplemobiletools.smsmessenger.helpers import android.content.Context import android.net.Uri -import android.provider.Telephony import android.provider.Telephony.* import android.util.Log import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.simplemobiletools.commons.extensions.queryCursor +import com.simplemobiletools.commons.extensions.showErrorToast import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.smsmessenger.extensions.* +import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.* import com.simplemobiletools.smsmessenger.models.ExportedMessage import java.io.File @@ -19,44 +20,50 @@ class MessagesImporter(private val context: Context) { } enum class ImportResult { - IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL, IMPORT_NOTHING_NEW + IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL } 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, callback: (result: ImportResult) -> Unit) { + fun importMessages(path: String, onProgress: (total: Int, current: Int) -> Unit = { _, _ -> }, callback: (result: ImportResult) -> Unit) { ensureBackgroundThread { - if (path.isEmpty()) { - callback.invoke(ImportResult.IMPORT_FAIL) - return@ensureBackgroundThread - } + try { - val inputStream = if (path.contains("/")) { - File(path).inputStream() - } else { - context.assets.open(path) - } + val inputStream = if (path.contains("/")) { + File(path).inputStream() + } else { + context.assets.open(path) + } - inputStream.bufferedReader().use { - try { - val json = it.readText() + inputStream.bufferedReader().use { reader -> + val json = reader.readText() Log.d(TAG, "importMessages: json== $json") val type = object : TypeToken>() {}.type val messages = gson.fromJson>(json, type) - Log.d(TAG, "importMessages: ${messages.size}") + val totalMessages = messages.flatMap { it.sms }.size + messages.flatMap { it.mms }.size + onProgress.invoke(totalMessages, messagesImported) for (message in messages) { // add sms if (config.importSms) { - message.sms.forEach(messageWriter::writeSmsMessage) + message.sms.forEach { backup -> + messageWriter.writeSmsMessage(backup) + messagesImported++ + onProgress.invoke(totalMessages, messagesImported) + } } // add mms if (config.importMms) { - message.mms.forEach(messageWriter::writeMmsMessage) + message.mms.forEach { backup -> + messageWriter.writeMmsMessage(backup) + messagesImported++ + onProgress.invoke(totalMessages, messagesImported) + } } - context.queryCursor(Threads.CONTENT_URI) { cursor -> val json = cursor.rowsToJson() Log.w(TAG, "converations = $json") @@ -78,13 +85,22 @@ class MessagesImporter(private val context: Context) { } refreshMessages() - callback.invoke(ImportResult.IMPORT_OK) } - } catch (e: Exception) { - Log.e(TAG, "importMessages: ", e) - callback.invoke(ImportResult.IMPORT_FAIL) } + } catch (e: Exception) { + context.showErrorToast(e) + messagesFailed++ } + + callback.invoke( + when { + messagesImported == 0 -> { + IMPORT_FAIL + } + messagesFailed > 0 -> IMPORT_PARTIAL + else -> 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 0d02c1be..505a6bcd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -107,7 +107,29 @@ class MessagesReader(private val context: Context) { 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)) + block( + MmsBackup( + creator, + contentType, + deliveryReport, + date, + dateSent, + locked, + messageType, + messageBox, + read, + readReport, + seen, + textOnly, + status, + subject, + subjectCharSet, + subscriptionId, + transactionId, + addresses, + parts + ) + ) } } @@ -216,7 +238,16 @@ class MessagesReader(private val context: Context) { return addresses } - fun getMessagesCount(): Long { - return 0 + fun getMessagesCount(): Int { + return countRows(Sms.CONTENT_URI) + countRows(Mms.CONTENT_URI) + } + + private fun countRows(uri: Uri): Int { + val cursor = context.contentResolver.query( + uri, null, null, null, null + ) ?: return 0 + cursor.use { + return cursor.count + } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt index 10c9d5a9..77f250e8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt @@ -42,8 +42,6 @@ class MessagesWriter(private val context: Context) { } else { Log.w(TAG, "SMS already exists") } - // update conversation date - updateThreadDate(threadId, smsBackup.date) } private fun updateThreadDate( @@ -86,7 +84,6 @@ class MessagesWriter(private val context: Context) { //write mms if (!mmsExist(mmsBackup)) { contentResolver.insert(Mms.CONTENT_URI, contentValues) - updateThreadDate(threadId, mmsBackup.date) } else { Log.w(TAG, "mms already exists") } @@ -215,8 +212,8 @@ class MessagesWriter(private val context: Context) { val uri = Uri.parse("content://mms/${messageId}/part") val projection = arrayOf(Mms.Part._ID) val selection = - "${Mms.Part.CONTENT_LOCATION} = ? AND ${Mms.Part.CT_TYPE} = ? AND ${Mms.Part.MSG_ID} = ? AND ${Mms.Part.CONTENT_ID} = ?" - val selectionArgs = arrayOf(mmsPart.contentLocation.toString(), mmsPart.contentType.toString(), messageId.toString(), mmsPart.contentId.toString()) + "${Mms.Part.CONTENT_LOCATION} = ? AND ${Mms.Part.CONTENT_TYPE} = ? AND ${Mms.Part.MSG_ID} = ? AND ${Mms.Part.CONTENT_ID} = ?" + val selectionArgs = arrayOf(mmsPart.contentLocation.toString(), mmsPart.contentType, messageId.toString(), mmsPart.contentId.toString()) var exists = false context.queryCursor(uri, projection, selection, selectionArgs) { exists = it.count > 0 From 04cac740eed1dd5eba95d535874927d0cb146079 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Sat, 18 Sep 2021 22:07:13 +0100 Subject: [PATCH 09/29] Cleanup code and remove logs --- .../smsmessenger/activities/MainActivity.kt | 6 +- .../dialogs/ImportMessagesDialog.kt | 13 +-- .../smsmessenger/helpers/JsonObjectWriter.kt | 43 --------- .../smsmessenger/helpers/MessagesExporter.kt | 15 --- .../smsmessenger/helpers/MessagesImporter.kt | 28 ------ .../smsmessenger/helpers/MessagesReader.kt | 45 ++------- .../smsmessenger/helpers/MessagesWriter.kt | 93 ++----------------- 7 files changed, 21 insertions(+), 222 deletions(-) delete mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.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 3adb9472..2339ed69 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -11,7 +11,6 @@ import android.graphics.drawable.LayerDrawable import android.net.Uri import android.os.Bundle import android.provider.Telephony -import android.util.Log import android.view.Menu import android.view.MenuItem import com.simplemobiletools.commons.dialogs.FilePickerDialog @@ -362,13 +361,10 @@ class MainActivity : SimpleActivity() { } } - private val TAG = "MainActivity" private fun exportMessagesTo(outputStream: OutputStream?) { ensureBackgroundThread { toast(R.string.exporting) - smsExporter.exportMessages(outputStream, { total, current -> - Log.d(TAG, "PERCENTAGE: ${current.toDouble() * 100 / total.toDouble()}%") - }) { + smsExporter.exportMessages(outputStream) { toast( when (it) { MessagesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful 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 36cf521c..ad5b14ba 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -1,6 +1,5 @@ package com.simplemobiletools.smsmessenger.dialogs -import android.util.Log import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.extensions.setupDialogStuff @@ -10,7 +9,9 @@ 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 com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_FAIL +import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_OK +import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_PARTIAL import kotlinx.android.synthetic.main.dialog_import_messages.view.import_mms_checkbox import kotlinx.android.synthetic.main.dialog_import_messages.view.import_sms_checkbox @@ -20,7 +21,6 @@ class ImportMessagesDialog( private val callback: (refresh: Boolean) -> Unit, ) { - private val TAG = "ImportMessagesDialog" private val config = activity.config init { @@ -39,13 +39,10 @@ class ImportMessagesDialog( config.importSms = view.import_sms_checkbox.isChecked config.importMms = view.import_mms_checkbox.isChecked ensureBackgroundThread { - MessagesImporter(activity).importMessages(path, onProgress = { total: Int, current: Int -> - Log.d(TAG, "PERCENTAGE: ${current.toDouble() * 100 / total.toDouble()}%") - }) { + MessagesImporter(activity).importMessages(path) { handleParseResult(it) + dismiss() } - dismiss() - callback.invoke(true) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt deleted file mode 100644 index 34b21fda..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/JsonObjectWriter.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.simplemobiletools.smsmessenger.helpers - -import com.google.gson.JsonArray -import com.google.gson.JsonNull -import com.google.gson.JsonObject -import com.google.gson.JsonPrimitive -import com.google.gson.stream.JsonWriter - -class JsonObjectWriter(private val writer: JsonWriter) { - - fun write(obj: JsonObject) { - writer.beginObject() - for (key in obj.keySet()) { - writer.name(key) - val keyObj = obj.get(key) - write(keyObj) - } - writer.endObject() - } - - private fun write(arr: JsonArray) { - writer.beginArray() - for (i in 0 until arr.size()) { - write(arr.get(i)) - } - writer.endArray() - } - - private fun write(obj: Any) { - when (obj) { - is JsonNull -> writer.nullValue() - is JsonPrimitive -> { - when{ - obj.isString -> writer.value(obj.asString) - obj.isBoolean -> writer.value(obj.asBoolean) - obj.isNumber -> writer.value(obj.asNumber) - } - } - is JsonArray -> write(obj) - is JsonObject -> write(obj) - } - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt index 609d837f..ce9e9bfe 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -23,21 +23,6 @@ class MessagesExporter(private val context: Context) { callback.invoke(ExportResult.EXPORT_FAIL) return@ensureBackgroundThread } - - /* - * We should have json in this format - * [ - * { - * "sms": [{ smses }], - * "mms": [{ mmses }] - * }, - * { - * "sms": [{ smses }], - * "mms": [{ mmses }] - * } - * ] - * - * */ val writer = JsonWriter(outputStream.bufferedWriter()) writer.use { try { 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 7d10c598..a24f1b76 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -15,10 +15,6 @@ import com.simplemobiletools.smsmessenger.models.ExportedMessage import java.io.File class MessagesImporter(private val context: Context) { - companion object { - private const val TAG = "MessagesImporter" - } - enum class ImportResult { IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL } @@ -41,13 +37,11 @@ class MessagesImporter(private val context: Context) { inputStream.bufferedReader().use { reader -> val json = reader.readText() - Log.d(TAG, "importMessages: json== $json") val type = object : TypeToken>() {}.type val messages = gson.fromJson>(json, type) val totalMessages = messages.flatMap { it.sms }.size + messages.flatMap { it.mms }.size onProgress.invoke(totalMessages, messagesImported) for (message in messages) { - // add sms if (config.importSms) { message.sms.forEach { backup -> messageWriter.writeSmsMessage(backup) @@ -55,7 +49,6 @@ class MessagesImporter(private val context: Context) { onProgress.invoke(totalMessages, messagesImported) } } - // add mms if (config.importMms) { message.mms.forEach { backup -> messageWriter.writeMmsMessage(backup) @@ -63,27 +56,6 @@ class MessagesImporter(private val context: Context) { onProgress.invoke(totalMessages, messagesImported) } } - - context.queryCursor(Threads.CONTENT_URI) { cursor -> - val json = cursor.rowsToJson() - Log.w(TAG, "converations = $json") - } - - context.queryCursor(Sms.CONTENT_URI) { cursor -> - val json = cursor.rowsToJson() - Log.w(TAG, "smses = $json") - } - - context.queryCursor(Mms.CONTENT_URI) { cursor -> - val json = cursor.rowsToJson() - Log.w(TAG, "mmses = $json") - } - - context.queryCursor(Uri.parse("content://mms/part")) { cursor -> - val json = cursor.rowsToJson() - Log.w(TAG, "parts = $json") - } - refreshMessages() } } 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 505a6bcd..8e54671f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -3,12 +3,11 @@ package com.simplemobiletools.smsmessenger.helpers import android.annotation.SuppressLint import android.content.Context import android.net.Uri -import android.provider.Telephony.* +import android.provider.Telephony.Mms +import android.provider.Telephony.Sms import android.util.Base64 -import android.util.Log import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.isQPlus -import com.simplemobiletools.smsmessenger.extensions.rowsToJson import com.simplemobiletools.smsmessenger.models.MmsAddress import com.simplemobiletools.smsmessenger.models.MmsBackup import com.simplemobiletools.smsmessenger.models.MmsPart @@ -17,10 +16,6 @@ import java.io.IOException import java.io.InputStream class MessagesReader(private val context: Context) { - companion object { - private const val TAG = "MessagesReader" - } - fun forEachSms(threadId: Long, block: (SmsBackup) -> Unit) { val projection = arrayOf( Sms.SUBSCRIPTION_ID, @@ -136,11 +131,7 @@ class MessagesReader(private val context: Context) { @SuppressLint("NewApi") private fun getParts(mmsId: Long): List { val parts = mutableListOf() - val uri = if (isQPlus()) { - Mms.Part.CONTENT_URI - } else { - Uri.parse("content://mms/part") - } + val uri = if (isQPlus()) Mms.Part.CONTENT_URI else Uri.parse("content://mms/part") val projection = arrayOf( Mms.Part._ID, Mms.Part.CONTENT_DISPOSITION, @@ -178,12 +169,7 @@ class MessagesReader(private val context: Context) { } else -> { usePart(partId) { stream -> - val arr = stream.readBytes() - Log.d(TAG, "getParts: $arr") - Log.d(TAG, "getParts: size = ${arr.size}") - Log.d(TAG, "getParts: US_ASCII-> ${arr.toString(Charsets.US_ASCII)}") - Log.d(TAG, "getParts: UTF_8-> ${arr.toString(Charsets.UTF_8)}") - Base64.encodeToString(arr, Base64.DEFAULT) + Base64.encodeToString(stream.readBytes(), Base64.DEFAULT) } } } @@ -194,24 +180,13 @@ class MessagesReader(private val context: Context) { @SuppressLint("NewApi") private fun usePart(partId: Long, block: (InputStream) -> String): String { - val partUri = if (isQPlus()) { - Mms.Part.CONTENT_URI.buildUpon().appendPath(partId.toString()).build() - } else { - Uri.parse("content://mms/part/$partId") - } + val partUri = if (isQPlus()) Mms.Part.CONTENT_URI.buildUpon().appendPath(partId.toString()).build() else Uri.parse("content://mms/part/$partId") try { - val stream = context.contentResolver.openInputStream(partUri) - if (stream == null) { - val msg = "failed opening stream for mms part $partUri" - Log.e(TAG, msg) - return "" - } + val stream = context.contentResolver.openInputStream(partUri) ?: return "" stream.use { return block(stream) } } catch (e: IOException) { - val msg = "failed to read MMS part on $partUri" - Log.e(TAG, msg, e) return "" } } @@ -219,22 +194,16 @@ class MessagesReader(private val context: Context) { @SuppressLint("NewApi") private fun getMMSAddresses(messageId: Long): List { val addresses = mutableListOf() - val uri = if (isQPlus()) { - Mms.Addr.getAddrUriForMessage(messageId.toString()) - } else { - Uri.parse("content://mms/$messageId/addr") - } + val uri = if (isQPlus()) Mms.Addr.getAddrUriForMessage(messageId.toString()) else Uri.parse("content://mms/$messageId/addr") val projection = arrayOf(Mms.Addr.ADDRESS, Mms.Addr.TYPE, Mms.Addr.CHARSET) val selection = "${Mms.Addr.MSG_ID}= ?" val selectionArgs = arrayOf(messageId.toString()) - context.queryCursor(uri, projection, selection, selectionArgs) { cursor -> val address = cursor.getStringValue(Mms.Addr.ADDRESS) val type = cursor.getIntValue(Mms.Addr.TYPE) val charset = cursor.getIntValue(Mms.Addr.CHARSET) addresses.add(MmsAddress(address, type, charset)) } - return addresses } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt index 77f250e8..89b53200 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt @@ -1,14 +1,11 @@ package com.simplemobiletools.smsmessenger.helpers import android.annotation.SuppressLint -import android.content.ContentValues import android.content.Context import android.net.Uri -import android.provider.Telephony -import android.provider.Telephony.* +import android.provider.Telephony.Mms +import android.provider.Telephony.Sms import android.util.Base64 -import android.util.Log -import androidx.core.content.contentValuesOf import com.google.android.mms.pdu_alt.PduHeaders import com.klinker.android.send_message.Utils import com.simplemobiletools.commons.extensions.getLongValue @@ -20,43 +17,18 @@ import com.simplemobiletools.smsmessenger.models.MmsPart import com.simplemobiletools.smsmessenger.models.SmsBackup class MessagesWriter(private val context: Context) { - companion object { - private const val INVALID_ID = -1L - private const val TAG = "MessagesWriter" - } - + private val INVALID_ID = -1L private val contentResolver = context.contentResolver fun writeSmsMessage(smsBackup: SmsBackup) { - Log.w(TAG, "writeSmsMessage: smsBackup=$smsBackup") val contentValues = smsBackup.toContentValues() val threadId = Utils.getOrCreateThreadId(context, smsBackup.address) contentValues.put(Sms.THREAD_ID, threadId) - Log.d(TAG, "writeSmsMessage: contentValues=$contentValues") - Log.d(TAG, "writeSmsMessage: type=${smsBackup.type}") if (!smsExist(smsBackup)) { - Log.d(TAG, "writeSmsMessage: Inserting SMS...") - val uri = Sms.CONTENT_URI - Log.d(TAG, "writeSmsMessage: uri=$uri") contentResolver.insert(Sms.CONTENT_URI, contentValues) - } else { - Log.w(TAG, "SMS already exists") } } - private fun updateThreadDate( - threadId: Long, - date: Long, - ) { - Log.d(TAG, "updateThreadDate: threadId=$threadId -- date=$date") - val selection = "${Threads._ID} = ?" - val selectionArgs = arrayOf(threadId.toString()) - val threadValues = contentValuesOf(Threads.DATE to date) - Log.d(TAG, "threadValues=$threadValues") - val result = contentResolver.update(Threads.CONTENT_URI.buildUpon().appendPath(threadId.toString()).build(), threadValues, null, null) - Log.d(TAG, "updateThreadDate: id=$result") - } - private fun smsExist(smsBackup: SmsBackup): Boolean { val uri = Sms.CONTENT_URI val projection = arrayOf(Sms._ID) @@ -65,9 +37,7 @@ class MessagesWriter(private val context: Context) { var exists = false context.queryCursor(uri, projection, selection, selectionArgs) { exists = it.count > 0 - Log.i(TAG, "smsExist After: $exists") } - Log.i(TAG, "smsExist: $exists") return exists } @@ -75,41 +45,25 @@ class MessagesWriter(private val context: Context) { // 1. write mms msg, get the msg_id, check if mms exists before writing // 2. write parts - parts depend on the msg id, check if part exist before writing, write data if it is a non-text part // 3. write the addresses, address depends on msg id too, check if address exist before writing - Log.w(TAG, "writeMmsMessage: backup=$mmsBackup") val contentValues = mmsBackup.toContentValues() val threadId = getMmsThreadId(mmsBackup) if (threadId != INVALID_ID) { contentValues.put(Mms.THREAD_ID, threadId) - Log.w(TAG, "writeMmsMessage: backup=$mmsBackup") - //write mms if (!mmsExist(mmsBackup)) { contentResolver.insert(Mms.CONTENT_URI, contentValues) - } else { - Log.w(TAG, "mms already exists") } val messageId = getMmsId(mmsBackup) if (messageId != INVALID_ID) { - Log.d(TAG, "writing mms addresses") - //write addresses - mmsBackup.addresses.forEach { writeMmsAddress(it, messageId) } mmsBackup.parts.forEach { writeMmsPart(it, messageId) } - } else { - Log.d(TAG, "failed to write mms message, invalid mms id") + mmsBackup.addresses.forEach { writeMmsAddress(it, messageId) } } - } else { - Log.d(TAG, "failed to write mms message, invalid thread id") } - } private fun getMmsThreadId(mmsBackup: MmsBackup): Long { val address = when (mmsBackup.messageBox) { - Mms.MESSAGE_BOX_INBOX -> { - mmsBackup.addresses.firstOrNull { it.type == PduHeaders.FROM }?.address - } - else -> { - mmsBackup.addresses.firstOrNull { it.type == PduHeaders.TO }?.address - } + Mms.MESSAGE_BOX_INBOX -> mmsBackup.addresses.firstOrNull { it.type == PduHeaders.FROM }?.address + else -> mmsBackup.addresses.firstOrNull { it.type == PduHeaders.TO }?.address } return if (!address.isNullOrEmpty()) { Utils.getOrCreateThreadId(context, address) @@ -124,13 +78,10 @@ class MessagesWriter(private val context: Context) { val projection = arrayOf(Mms._ID) val selection = "${Mms.DATE} = ? AND ${Mms.DATE_SENT} = ? AND ${Mms.THREAD_ID} = ? AND ${Mms.MESSAGE_BOX} = ?" val selectionArgs = arrayOf(mmsBackup.date.toString(), mmsBackup.dateSent.toString(), threadId.toString(), mmsBackup.messageBox.toString()) - var id = INVALID_ID context.queryCursor(uri, projection, selection, selectionArgs) { id = it.getLongValue(Mms._ID) - Log.i(TAG, "getMmsId After: $id") } - Log.i(TAG, "getMmsId: $id") return id } @@ -141,22 +92,14 @@ class MessagesWriter(private val context: Context) { @SuppressLint("NewApi") private fun mmsAddressExist(mmsAddress: MmsAddress, messageId: Long): Boolean { - val addressUri = if (isQPlus()) { - Mms.Addr.getAddrUriForMessage(messageId.toString()) - } else { - Uri.parse("content://mms/$messageId/addr") - } + val addressUri = if (isQPlus()) Mms.Addr.getAddrUriForMessage(messageId.toString()) else Uri.parse("content://mms/$messageId/addr") val projection = arrayOf(Mms.Addr._ID) val selection = "${Mms.Addr.TYPE} = ? AND ${Mms.Addr.ADDRESS} = ? AND ${Mms.Addr.MSG_ID} = ?" val selectionArgs = arrayOf(mmsAddress.type.toString(), mmsAddress.address.toString(), messageId.toString()) - var exists = false context.queryCursor(addressUri, projection, selection, selectionArgs) { exists = it.count > 0 - Log.i(TAG, "mmsAddressExist After: $exists") } - Log.i(TAG, "mmsAddressExist: $exists") - return exists } @@ -172,37 +115,27 @@ class MessagesWriter(private val context: Context) { val contentValues = mmsAddress.toContentValues() contentValues.put(Mms.Addr.MSG_ID, messageId) contentResolver.insert(addressUri, contentValues) - } else { - Log.w(TAG, "writeMmsAddress: Skip already exists") } } @SuppressLint("NewApi") private fun writeMmsPart(mmsPart: MmsPart, messageId: Long) { - Log.d(TAG, "writeMmsPart: Writing part= $mmsPart") if (!mmsPartExist(mmsPart, messageId)) { val uri = Uri.parse("content://mms/${messageId}/part") val contentValues = mmsPart.toContentValues() contentValues.put(Mms.Part.MSG_ID, messageId) val partUri = contentResolver.insert(uri, contentValues) - //write data - Log.d(TAG, "writeMmsPart: Inserted part=$partUri") try { if (partUri != null) { if (mmsPart.isNonText()) { contentResolver.openOutputStream(partUri).use { val arr = Base64.decode(mmsPart.data, Base64.DEFAULT) it!!.write(arr) - Log.d(TAG, "Wrote part data $mmsPart") } - } else { - Log.w(TAG, "skip writing text content") } - } else { - Log.e(TAG, "invalid uri while writing part") } } catch (e: Exception) { - Log.e(TAG, "writeMmsPart: uri", e) + } } } @@ -217,17 +150,7 @@ class MessagesWriter(private val context: Context) { var exists = false context.queryCursor(uri, projection, selection, selectionArgs) { exists = it.count > 0 - Log.i(TAG, "mmsPartExist After: $exists") } - Log.i(TAG, "mmsPartExist: $exists") - return exists } - - fun updateAllSmsThreads() { - // thread dates + states might be wrong, we need to force a full update - // unfortunately there's no direct way to do that in the SDK, but passing a - // negative conversation id to delete should to the trick - contentResolver.delete(Sms.Conversations.CONTENT_URI.buildUpon().appendPath("-1").build(), null, null) - } } From 98cd90b5f20997d5228ed7e1912b4a4a8280f648 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Sat, 18 Sep 2021 22:36:19 +0100 Subject: [PATCH 10/29] Update strings --- app/src/main/res/values-cs/strings.xml | 11 +++++++++-- app/src/main/res/values-da/strings.xml | 11 +++++++++-- app/src/main/res/values-de/strings.xml | 11 +++++++++-- app/src/main/res/values-el/strings.xml | 11 +++++++++-- app/src/main/res/values-es/strings.xml | 11 +++++++++-- app/src/main/res/values-fi/strings.xml | 11 +++++++++-- app/src/main/res/values-fr/strings.xml | 11 +++++++++-- app/src/main/res/values-gl/strings.xml | 11 +++++++++-- app/src/main/res/values-id/strings.xml | 11 +++++++++-- app/src/main/res/values-it/strings.xml | 11 +++++++++-- app/src/main/res/values-ja/strings.xml | 11 +++++++++-- app/src/main/res/values-lt/strings.xml | 11 +++++++++-- app/src/main/res/values-ml/strings.xml | 11 +++++++++-- app/src/main/res/values-nl/strings.xml | 11 +++++++++-- app/src/main/res/values-pl/strings.xml | 11 +++++++++-- app/src/main/res/values-pt/strings.xml | 11 +++++++++-- app/src/main/res/values-ru/strings.xml | 11 +++++++++-- app/src/main/res/values-sk/strings.xml | 11 +++++++++-- app/src/main/res/values-tr/strings.xml | 11 +++++++++-- app/src/main/res/values-uk/strings.xml | 11 +++++++++-- app/src/main/res/values-zh-rCN/strings.xml | 11 +++++++++-- 21 files changed, 189 insertions(+), 42 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index af8271c5..e7d62ee2 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -15,8 +15,6 @@ Odesílatel nepodporuje odpovědi Návrh Odesílá se… - Export zpráv - Import zpráv Nová konverzace @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Export zpráv + Messages + Export SMS + Export MMS + Import zpráv + Import SMS + Import MMS + Proč aplikace vyžaduje přístup k internetu? Je smutné, že je to nutné pro odesílání příloh MMS. Nebýt schopen posílat MMS by byla opravdu obrovská nevýhoda ve srovnání s jinými aplikacemi, proto jsme se rozhodli jít touto cestou. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 16102277..4529678b 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -15,8 +15,6 @@ Afsender understøtter ikke svar Udkast Sender… - Eksporter beskeder - Importer beskeder Ny Samtale @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Eksporter beskeder + Export SMS + Export MMS + Importer beskeder + Import SMS + Import MMS + Hvorfor kræver appen adgang til internettet? Desværre er det nødvendigt for at sende MMS-vedhæftede filer. Ikke at kunne være i stand til at sende MMS ville være en virkelig stor ulempe i forhold til andre apps, så vi besluttede at gå denne vej. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 40f69e22..9f3401cc 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -15,8 +15,6 @@ Der Absender unterstützt keine Antworten. Entwurf Sende… - Export messages - Import messages Neuer Chat @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + Warum benötigt diese App Internetzugriff? Leider ist dies nötig, um MMS-Anhänge zu versenden. Es wäre ein großer Nachteil gegenüber anderen Apps, wenn keine MMS versendet werden könnten, also haben wir uns für diesen Weg entschieden. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index ec01e170..8c504e92 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -15,8 +15,6 @@ Ο αποστολέας δεν υποστηρίζει απαντήσεις Πρόχειρο Γίνεται αποστολή… - Εξαγωγή μηνυμάτων - Εισαγωγή μηνυμάτων Νέα συνομιλία @@ -53,6 +51,15 @@ Αλλαγή μεγέθους απεσταλμένων εικόνων MMS Χωρίς όριο + + Messages + Εξαγωγή μηνυμάτων + Export SMS + Export MMS + Εισαγωγή μηνυμάτων + Import SMS + Import MMS + Γιατί η εφαρμογή απαιτεί πρόσβαση στο Internet; Δυστυχώς, απαιτείται για την αποστολή συνημμένων MMS. Το να μην είμαστε σε θέση να στείλουμε MMS θα αποτελούσε πραγματικά τεράστιο μειονέκτημα σε σύγκριση με άλλες εφαρμογές, επομένως αποφασίσαμε να ακολουθήσουμε αυτόν τον δρόμο. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index a267726e..af192274 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages Nueva conversación @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + ¿Por qué la aplicación requiere acceso a internet? Tristemente es necesitado para enviar archivos adjuntos MMS. El no poder enviar MMS sería una desventaja realmente enorme comparada con otras aplicaciones, así que decidimos tomar este camino. diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index ab3a4416..858006f1 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -15,8 +15,6 @@ Lähettäjä ei tue vastauksia Luonnos Lähetetään… - Export messages - Import messages Uusi keskustelu @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + Miksi sovellus vaatii Internet-yhteyden? Valitettavasti sitä tarvitaan multimediaviestin-liitteiden lähettämiseen. Multimediaviestien lähettämättä jättäminen olisi todella valtava haitta muihin sovelluksiin verrattuna, joten päätimme mennä tällä tavalla. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 1625d4fd..9d3d90e2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -15,8 +15,6 @@ L\'expéditeur n\'accepte pas les réponses Brouillon Envoi en cours… - Export de messages - Import de messages Nouvelle conversation @@ -53,6 +51,15 @@ Redimensionner les images MMS envoyées Pas de limite + + Messages + Export de messages + Export SMS + Export MMS + Import de messages + Import SMS + Import MMS + Pourquoi cette application a besoin d\'un accès à internet ? Malheureusement, cela est nécessaire pour envoyer des pièces jointes dans les MMS. Ne pas pouvoir envoyer de MMS serait un énorme désavantage comparé à d\'autres applications, nous avons donc décidé de faire ainsi. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index d566febf..f2df0f2a 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages Nova conversa @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + Por que o aplicativo necesita acceder a Internet? Infelizmente é a única forma para poder enviar anexos MMS. A incapacidade de non conseguir enviar MMS sería unha enorme desvantaxe comparativamente a outros aplicativos e, por iso, tomamos esta decisión. Pero, como habitualmente, o aplicativo non ten anuncios, non rastrea os utilizadores nin recolle datos persoais. Este permiso só é necesario para enviar as MMS. diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index e4217469..6aeb25a1 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages Percakapan baru @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + Mengapa aplikasi membutuhkan akses ke internet? Sayangnya itu diperlukan untuk mengirim lampiran MMS. Tidak dapat mengirim MMS akan menjadi kerugian yang sangat besar dibandingkan dengan aplikasi lain, jadi kami memutuskan untuk menggunakan cara ini. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b1477e50..6224ec79 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -15,8 +15,6 @@ Il mittente non accetta risposte Bozza Invio… - Export messages - Import messages Nuova conversazione @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + Perché l\'applicazione richiede l\'accesso ad internet? Purtroppo è necessario per poter inviare gli allegati degli MMS. Non essere in grado di inviare gli MMS sarebbe un grosso svantaggio in confronto ad altre applicazioni, quindi abbiamo deciso di intraprendere questa strada. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index b281ca3d..b0bd9d9e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages 新しい会話 @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + なぜアプリ使用にインターネットへのアクセスが必要なのですか? 生憎、MMS(マルチメディアメッセージサービス)にインターネットが必要となります。他のアプリと比較して、MMSを使用出来ないと大きな損になるので、こうすることに決めました。 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 714051b0..df287350 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages Naujas pokalbis @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + Why does the app require access to the internet? Sadly it is needed for sending MMS attachments. Not being able to send MMS would be a really huge disadvantage compared to other apps, so we decided to go this way. diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 50c2029f..826b61e6 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages പുതിയ സംഭാഷണം @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + അപ്ലിക്കേഷന് ഇന്റർനെറ്റിലേക്ക് ആവശ്യമായി വരുന്നത് എന്തുകൊണ്ട്? നിർഭാഗ്യവശാൽ, MMS അറ്റാച്ചുമെന്റുകൾ അയക്കുന്നതിനു ഇത് ആവശ്യമാണ്. മറ്റ് ആപ്ലിക്കേഷനുകളുമായി താരതമ്യപ്പെടുത്തുമ്പോൾ MMS അയയ്ക്കാൻ കഴിയുന്നില്ല എന്നത് ഒരു വലിയ പോരായ്മയാണ്, അതിനാൽ ഞങ്ങൾ ഈ റൂട്ടിൽ പോകാൻ തീരുമാനിച്ചു. diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 759d3c56..6ccbd17b 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -15,8 +15,6 @@ Afzender ondersteunt geen antwoorden Concept Versturen… - Berichten exporteren - Berichten importeren Nieuw gesprek @@ -53,6 +51,15 @@ Afbeelding verkleinen voor MMS Geen limiet + + Messages + Berichten exporteren + Export SMS + Export MMS + Berichten importeren + Import SMS + Import MMS + Waarom heeft deze app toegang nodig tot het internet? Dit is helaas nodig voor het verzenden van MMS-bijlagen. Het versturen van MMS-berichten onmogelijk maken zou een te groot nadeel t.o.v. andere apps betekenen en daarom hebben we besloten om het toch toe te voegen. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index bd91ca19..3cd8ac2b 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -15,8 +15,6 @@ Nadawca nie obsługuje odpowiedzi Szkic Wysyłanie… - Eksportuj wiadomości - Importuj wiadomości Nowa rozmowa @@ -55,6 +53,15 @@ Rozmiar wysyłanych obrazków w MMS-ach Bez limitu + + Messages + Eksportuj wiadomości + Export SMS + Export MMS + Importuj wiadomości + Import SMS + Import MMS + Dlaczego aplikacja wymaga dostępu do Internetu? Niestety jest to konieczne do wysyłania załączników MMS. Brak możliwości wysyłania MMS-ów byłby naprawdę ogromną wadą w porównaniu z innymi aplikacjami, więc zdecydowaliśmy się pójść tą drogą. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 4f08f40e..6e463c16 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -15,8 +15,6 @@ O remetente não aceita respostas Rascunho A enviar… - Exportar mensagens - Importar mensagens Nova conversa @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Exportar mensagens + Export SMS + Export MMS + Importar mensagens + Import SMS + Import MMS + Porque é que a aplicação necessita de aceder à Internet? Infelizmente é a única forma para poder enviar anexos MMS. A incapacidade de não conseguir enviar MMS seria uma enorme desvantagem comparativamente a outras aplicações e, por isso, tomámos esta decisão. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c4ae0a0b..59e27e03 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -15,8 +15,6 @@ Отправитель не поддерживает ответы Черновик Отправка… - Экспорт сообщений - Импорт сообщений Новая переписка @@ -55,6 +53,15 @@ Изменять размер отправляемых в MMS изображений Нет ограничения + + Messages + Экспорт сообщений + Export SMS + Export MMS + Импорт сообщений + Import SMS + Import MMS + Почему приложение требует доступ к интернету? К сожалению, это необходимо для отправки вложений MMS. Отсутствие возможности отправлять MMS-сообщения было бы огромным недостатком нашего приложения по сравнению с другими, поэтому мы решили пойти этим путём. diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 5c922a60..450140b2 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -15,8 +15,6 @@ Odosielateľ nepodporuje odpovede Koncept Odosiela sa… - Exportovať správy - Importovať správy Nová konverzácia @@ -55,6 +53,15 @@ Zmenšiť MMS obrázky pri odosielaní Žiadny limit + + Messages + Exportovať správy + Export SMS + Export MMS + Importovať správy + Import SMS + Import MMS + Prečo vyžaduje apka prístup na internet? Je to žiaľ nevyhnutné pre odosielanie MMS príloh. Ak by sa ich nedalo odosielať, bola by to obrovská nevýhoda v porovnaní s konkurenciou, preto sme sa rozhodli takto. diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 575b3c20..ae654fd9 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages Yeni görüşme @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + Uygulama neden internete erişim gerektiriyor? Ne yazık ki MMS eklerini göndermek için gerekli. MMS gönderememek, diğer uygulamalara kıyasla gerçekten çok büyük bir dezavantaj olacaktır, bu yüzden bu şekilde gitmeye karar verdik. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8a5a6b46..96b32426 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages Нове листування @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + Чому додаток потрубує доступу до інтернету? Нажаль, це необхідно для відправки вкладень MMS. Неспроможність надсилати MMS-повідомлення була б великим недоліком нашого додатку порівняно з іншими, тому ми так зробили. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index c2622ff6..2baa5eff 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -15,8 +15,6 @@ Sender doesn\'t support replies Draft Sending… - Export messages - Import messages 新的对话 @@ -53,6 +51,15 @@ Resize sent MMS images No limit + + Messages + Export messages + Export SMS + Export MMS + Import messages + Import SMS + Import MMS + 为什么该应用需要访问互联网? 很遗憾这对于发送彩信附件是必须的。如果不能发送彩信的话这相比其他应用会是一个巨大的劣势,所以我们决定这么采取现在的方式。 From 00b97dd639bd178f445b7f290d995fd73b2dd578 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Mon, 20 Sep 2021 22:27:03 +0200 Subject: [PATCH 11/29] Update dialog_export_messages.xml --- app/src/main/res/layout/dialog_export_messages.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_export_messages.xml b/app/src/main/res/layout/dialog_export_messages.xml index 1c2fda39..fe3b6fd1 100644 --- a/app/src/main/res/layout/dialog_export_messages.xml +++ b/app/src/main/res/layout/dialog_export_messages.xml @@ -34,7 +34,7 @@ android:id="@+id/export_messages_filename_label" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/filename" + android:text="@string/filename_without_txt" android:textSize="@dimen/smaller_text_size" /> Date: Mon, 20 Sep 2021 22:28:43 +0200 Subject: [PATCH 12/29] updating commons --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ab02e588..28e7c305 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -56,7 +56,7 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:f49f7b5f89' + implementation 'com.github.SimpleMobileTools:Simple-Commons:bef619280b' implementation 'org.greenrobot:eventbus:3.2.0' implementation 'com.klinkerapps:android-smsmms:5.2.6' implementation 'com.github.tibbi:IndicatorFastScroll:c3de1d040a' From e033dd284ab2603e50d50a14bb8a85bba786f41c Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Mon, 20 Sep 2021 22:34:50 +0200 Subject: [PATCH 13/29] removing some useles comments --- .../simplemobiletools/smsmessenger/helpers/MessagesExporter.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt index ce9e9bfe..3b6693f3 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -35,7 +35,6 @@ class MessagesExporter(private val context: Context) { if (config.exportSms) { writer.name("sms") writer.beginArray() - //write all sms messageReader.forEachSms(threadId) { writer.jsonValue(gson.toJson(it)) written++ @@ -47,7 +46,6 @@ class MessagesExporter(private val context: Context) { if (config.exportMms) { writer.name("mms") writer.beginArray() - //write all mms messageReader.forEachMms(threadId) { writer.jsonValue(gson.toJson(it)) written++ From 4243679f2d7302fdc8002e508637ef95a289952a Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Mon, 20 Sep 2021 22:40:40 +0200 Subject: [PATCH 14/29] updating some slovak strings --- app/src/main/res/values-sk/strings.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 450140b2..877146e6 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -54,13 +54,13 @@ Žiadny limit - Messages + Správy Exportovať správy - Export SMS - Export MMS + Exportovať SMS + Exportovať MMS Importovať správy - Import SMS - Import MMS + Importovať SMS + Importovať MMS Prečo vyžaduje apka prístup na internet? From 3ed3ce2a9b284f8286ad8205f40b4e1b66d02589 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Tue, 21 Sep 2021 10:14:38 +0200 Subject: [PATCH 15/29] Update build.gradle --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 28e7c305..c180d536 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -56,7 +56,7 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:bef619280b' + implementation 'com.github.SimpleMobileTools:Simple-Commons:5c11cc9a8e' implementation 'org.greenrobot:eventbus:3.2.0' implementation 'com.klinkerapps:android-smsmms:5.2.6' implementation 'com.github.tibbi:IndicatorFastScroll:c3de1d040a' From 72f0e27655d8ae5d89f9e9f591e715bd964d5622 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Tue, 21 Sep 2021 10:15:33 +0200 Subject: [PATCH 16/29] Make sure the Settings menu button is above About --- app/src/main/res/menu/menu_main.xml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index 3e772ac1..70a319fe 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -6,20 +6,18 @@ android:icon="@drawable/ic_search_vector" android:title="@string/search" app:showAsAction="always" /> - - - + app:showAsAction="never" /> + app:showAsAction="never" /> + Date: Tue, 21 Sep 2021 12:00:05 +0200 Subject: [PATCH 17/29] Update dialog_export_messages.xml --- app/src/main/res/layout/dialog_export_messages.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_export_messages.xml b/app/src/main/res/layout/dialog_export_messages.xml index fe3b6fd1..17aa0663 100644 --- a/app/src/main/res/layout/dialog_export_messages.xml +++ b/app/src/main/res/layout/dialog_export_messages.xml @@ -41,7 +41,7 @@ android:id="@+id/export_messages_filename" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/activity_margin" + android:layout_marginStart="@dimen/small_margin" android:layout_marginBottom="@dimen/activity_margin" android:paddingTop="@dimen/normal_margin" android:paddingEnd="@dimen/small_margin" From ea9c317a11894f718716c811f33eb28e71fd6f2c Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Tue, 21 Sep 2021 12:01:41 +0200 Subject: [PATCH 18/29] correcting an Android OS version check --- .../smsmessenger/helpers/MessagesReader.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 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 8e54671f..4c62c8c1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -8,6 +8,7 @@ import android.provider.Telephony.Sms 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 @@ -30,6 +31,7 @@ class MessagesReader(private val context: Context) { Sms.TYPE, Sms.SERVICE_CENTER ) + val selection = "${Sms.THREAD_ID} = ?" val selectionArgs = arrayOf(threadId.toString()) context.queryCursor(Sms.CONTENT_URI, projection, selection, selectionArgs) { cursor -> @@ -69,6 +71,7 @@ class MessagesReader(private val context: Context) { Mms.SUBSCRIPTION_ID, Mms.TRANSACTION_ID ) + val selection = if (includeTextOnlyAttachment) { "${Mms.THREAD_ID} = ? AND ${Mms.TEXT_ONLY} = ?" } else { @@ -80,6 +83,7 @@ class MessagesReader(private val context: Context) { } else { arrayOf(threadId.toString()) } + context.queryCursor(Mms.CONTENT_URI, projection, selection, selectionArgs) { cursor -> val mmsId = cursor.getLongValue(Mms._ID) val creator = cursor.getStringValueOrNull(Mms.CREATOR) @@ -146,7 +150,8 @@ class MessagesReader(private val context: Context) { Mms.Part.SEQ, Mms.Part.TEXT ) - val selection = "${Mms.Part.MSG_ID}= ?" + + val selection = "${Mms.Part.MSG_ID} = ?" val selectionArgs = arrayOf(mmsId.toString()) context.queryCursor(uri, projection, selection, selectionArgs) { cursor -> val partId = cursor.getLongValue(Mms.Part._ID) @@ -194,7 +199,7 @@ class MessagesReader(private val context: Context) { @SuppressLint("NewApi") private fun getMMSAddresses(messageId: Long): List { val addresses = mutableListOf() - val uri = if (isQPlus()) Mms.Addr.getAddrUriForMessage(messageId.toString()) else Uri.parse("content://mms/$messageId/addr") + val uri = if (isRPlus()) Mms.Addr.getAddrUriForMessage(messageId.toString()) else Uri.parse("content://mms/$messageId/addr") val projection = arrayOf(Mms.Addr.ADDRESS, Mms.Addr.TYPE, Mms.Addr.CHARSET) val selection = "${Mms.Addr.MSG_ID}= ?" val selectionArgs = arrayOf(messageId.toString()) From cb1affb711616fbae3ef2b91f9b1e7224d4d4e76 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Tue, 21 Sep 2021 12:03:07 +0200 Subject: [PATCH 19/29] correcting an Android OS version check --- .../smsmessenger/helpers/MessagesWriter.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt index 89b53200..6065362d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesWriter.kt @@ -10,7 +10,7 @@ import com.google.android.mms.pdu_alt.PduHeaders import com.klinker.android.send_message.Utils import com.simplemobiletools.commons.extensions.getLongValue import com.simplemobiletools.commons.extensions.queryCursor -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 @@ -92,7 +92,7 @@ class MessagesWriter(private val context: Context) { @SuppressLint("NewApi") private fun mmsAddressExist(mmsAddress: MmsAddress, messageId: Long): Boolean { - val addressUri = if (isQPlus()) Mms.Addr.getAddrUriForMessage(messageId.toString()) else Uri.parse("content://mms/$messageId/addr") + val addressUri = if (isRPlus()) Mms.Addr.getAddrUriForMessage(messageId.toString()) else Uri.parse("content://mms/$messageId/addr") val projection = arrayOf(Mms.Addr._ID) val selection = "${Mms.Addr.TYPE} = ? AND ${Mms.Addr.ADDRESS} = ? AND ${Mms.Addr.MSG_ID} = ?" val selectionArgs = arrayOf(mmsAddress.type.toString(), mmsAddress.address.toString(), messageId.toString()) @@ -106,7 +106,7 @@ class MessagesWriter(private val context: Context) { @SuppressLint("NewApi") private fun writeMmsAddress(mmsAddress: MmsAddress, messageId: Long) { if (!mmsAddressExist(mmsAddress, messageId)) { - val addressUri = if (isQPlus()) { + val addressUri = if (isRPlus()) { Mms.Addr.getAddrUriForMessage(messageId.toString()) } else { Uri.parse("content://mms/$messageId/addr") @@ -144,8 +144,7 @@ class MessagesWriter(private val context: Context) { private fun mmsPartExist(mmsPart: MmsPart, messageId: Long): Boolean { val uri = Uri.parse("content://mms/${messageId}/part") val projection = arrayOf(Mms.Part._ID) - val selection = - "${Mms.Part.CONTENT_LOCATION} = ? AND ${Mms.Part.CONTENT_TYPE} = ? AND ${Mms.Part.MSG_ID} = ? AND ${Mms.Part.CONTENT_ID} = ?" + val selection = "${Mms.Part.CONTENT_LOCATION} = ? AND ${Mms.Part.CONTENT_TYPE} = ? AND ${Mms.Part.MSG_ID} = ? AND ${Mms.Part.CONTENT_ID} = ?" val selectionArgs = arrayOf(mmsPart.contentLocation.toString(), mmsPart.contentType, messageId.toString(), mmsPart.contentId.toString()) var exists = false context.queryCursor(uri, projection, selection, selectionArgs) { From 41d9d067cf8cec42b626e68cb571da539a98290b Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Tue, 21 Sep 2021 12:07:34 +0200 Subject: [PATCH 20/29] Update build.gradle --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c180d536..54a67a86 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -56,7 +56,7 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:5c11cc9a8e' + implementation 'com.github.SimpleMobileTools:Simple-Commons:540c8c39ba' implementation 'org.greenrobot:eventbus:3.2.0' implementation 'com.klinkerapps:android-smsmms:5.2.6' implementation 'com.github.tibbi:IndicatorFastScroll:c3de1d040a' From 27180c60533a7df8e3945f1a9f6242baab60d385 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Tue, 21 Sep 2021 12:08:04 +0200 Subject: [PATCH 21/29] correcting the file extension label from txt to json --- app/src/main/res/layout/dialog_export_messages.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_export_messages.xml b/app/src/main/res/layout/dialog_export_messages.xml index 17aa0663..510967dd 100644 --- a/app/src/main/res/layout/dialog_export_messages.xml +++ b/app/src/main/res/layout/dialog_export_messages.xml @@ -34,7 +34,7 @@ android:id="@+id/export_messages_filename_label" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/filename_without_txt" + android:text="@string/filename_without_json" android:textSize="@dimen/smaller_text_size" /> Date: Tue, 21 Sep 2021 15:19:09 +0100 Subject: [PATCH 22/29] Handle cases where sms or mms does not exist in import or export --- .../smsmessenger/helpers/MessagesExporter.kt | 4 ++-- .../smsmessenger/helpers/MessagesImporter.kt | 12 ++++++++---- .../smsmessenger/helpers/MessagesReader.kt | 14 +++++++++++--- .../smsmessenger/models/ExportedMessage.kt | 4 ++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt index 3b6693f3..49c2d4b6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesExporter.kt @@ -32,7 +32,7 @@ class MessagesExporter(private val context: Context) { val totalMessages = messageReader.getMessagesCount() for (threadId in conversationIds) { writer.beginObject() - if (config.exportSms) { + if (config.exportSms && messageReader.getSmsCount() > 0) { writer.name("sms") writer.beginArray() messageReader.forEachSms(threadId) { @@ -43,7 +43,7 @@ class MessagesExporter(private val context: Context) { writer.endArray() } - if (config.exportMms) { + if (config.exportMms && messageReader.getMmsCount() > 0) { writer.name("mms") writer.beginArray() messageReader.forEachMms(threadId) { 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 a24f1b76..167b191c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -16,7 +16,7 @@ import java.io.File class MessagesImporter(private val context: Context) { enum class ImportResult { - IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL + IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL, IMPORT_NOTHING_NEW } private val gson = Gson() @@ -39,18 +39,22 @@ class MessagesImporter(private val context: Context) { val json = reader.readText() val type = object : TypeToken>() {}.type val messages = gson.fromJson>(json, type) - val totalMessages = messages.flatMap { it.sms }.size + messages.flatMap { it.mms }.size + val totalMessages = messages.flatMap { it.sms ?: emptyList() }.size + messages.flatMap { it.mms ?: emptyList() }.size + if (totalMessages <= 0) { + callback.invoke(IMPORT_NOTHING_NEW) + return@ensureBackgroundThread + } onProgress.invoke(totalMessages, messagesImported) for (message in messages) { if (config.importSms) { - message.sms.forEach { backup -> + message.sms?.forEach { backup -> messageWriter.writeSmsMessage(backup) messagesImported++ onProgress.invoke(totalMessages, messagesImported) } } if (config.importMms) { - message.mms.forEach { backup -> + message.mms?.forEach { backup -> messageWriter.writeMmsMessage(backup) messagesImported++ onProgress.invoke(totalMessages, messagesImported) 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 4c62c8c1..175ad735 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesReader.kt @@ -105,7 +105,7 @@ class MessagesReader(private val context: Context) { val transactionId = cursor.getStringValueOrNull(Mms.TRANSACTION_ID) val parts = getParts(mmsId) - val addresses = getMMSAddresses(mmsId) + val addresses = getMmsAddresses(mmsId) block( MmsBackup( creator, @@ -197,7 +197,7 @@ class MessagesReader(private val context: Context) { } @SuppressLint("NewApi") - private fun getMMSAddresses(messageId: Long): List { + private fun getMmsAddresses(messageId: Long): List { val addresses = mutableListOf() val uri = if (isRPlus()) Mms.Addr.getAddrUriForMessage(messageId.toString()) else Uri.parse("content://mms/$messageId/addr") val projection = arrayOf(Mms.Addr.ADDRESS, Mms.Addr.TYPE, Mms.Addr.CHARSET) @@ -213,7 +213,15 @@ class MessagesReader(private val context: Context) { } fun getMessagesCount(): Int { - return countRows(Sms.CONTENT_URI) + countRows(Mms.CONTENT_URI) + return getSmsCount() + getMmsCount() + } + + fun getMmsCount(): Int { + return countRows(Mms.CONTENT_URI) + } + + fun getSmsCount(): Int { + return countRows(Sms.CONTENT_URI) } private fun countRows(uri: Uri): Int { diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt index 35a5fddc..06253d48 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ExportedMessage.kt @@ -4,7 +4,7 @@ import com.google.gson.annotations.SerializedName data class ExportedMessage( @SerializedName("sms") - val sms: List, + val sms: List?, @SerializedName("mms") - val mms: List, + val mms: List?, ) From 3cd84e42bc52baa2d23a2d0e623a77d37013b137 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Tue, 21 Sep 2021 15:29:38 +0100 Subject: [PATCH 23/29] Handle validation where no export options are checked --- .../smsmessenger/dialogs/ExportMessagesDialog.kt | 5 +++++ .../smsmessenger/dialogs/ImportMessagesDialog.kt | 5 +++++ app/src/main/res/values-cs/strings.xml | 2 ++ app/src/main/res/values-da/strings.xml | 2 ++ app/src/main/res/values-de/strings.xml | 2 ++ app/src/main/res/values-el/strings.xml | 2 ++ app/src/main/res/values-es/strings.xml | 2 ++ app/src/main/res/values-fi/strings.xml | 2 ++ app/src/main/res/values-fr/strings.xml | 2 ++ app/src/main/res/values-gl/strings.xml | 2 ++ app/src/main/res/values-id/strings.xml | 2 ++ app/src/main/res/values-it/strings.xml | 2 ++ app/src/main/res/values-ja/strings.xml | 2 ++ app/src/main/res/values-lt/strings.xml | 2 ++ app/src/main/res/values-ml/strings.xml | 2 ++ app/src/main/res/values-nl/strings.xml | 2 ++ app/src/main/res/values-pl/strings.xml | 2 ++ app/src/main/res/values-pt/strings.xml | 2 ++ app/src/main/res/values-ru/strings.xml | 2 ++ app/src/main/res/values-sk/strings.xml | 2 ++ app/src/main/res/values-tr/strings.xml | 2 ++ app/src/main/res/values-uk/strings.xml | 2 ++ app/src/main/res/values-zh-rCN/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 24 files changed, 54 insertions(+) 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 7a52a6ad..774c1250 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ExportMessagesDialog.kt @@ -57,6 +57,11 @@ class ExportMessagesDialog( return@setOnClickListener } + if(!view.export_sms_checkbox.isChecked && !view.export_mms_checkbox.isChecked){ + activity.toast(R.string.export_unchecked_error_message) + return@setOnClickListener + } + config.exportSms = view.export_sms_checkbox.isChecked config.exportMms = view.export_mms_checkbox.isChecked config.lastExportPath = file.absolutePath.getParentPath() 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 ad5b14ba..f968498f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -35,6 +35,11 @@ class ImportMessagesDialog( .create().apply { activity.setupDialogStuff(view, this, R.string.import_messages) { getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + if(!view.import_sms_checkbox.isChecked && !view.import_mms_checkbox.isChecked){ + activity.toast(R.string.import_unchecked_error_message) + return@setOnClickListener + } + activity.toast(R.string.importing) config.importSms = view.import_sms_checkbox.isChecked config.importMms = view.import_mms_checkbox.isChecked diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index e7d62ee2..d3c964b0 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -56,9 +56,11 @@ Messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import zpráv Import SMS Import MMS + Check at least one of Import SMS or Import MMS Proč aplikace vyžaduje přístup k internetu? diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 4529678b..6014767f 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -56,9 +56,11 @@ Eksporter beskeder Export SMS Export MMS + Check at least one of Export SMS or Export MMS Importer beskeder Import SMS Import MMS + Check at least one of Import SMS or Import MMS Hvorfor kræver appen adgang til internettet? diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 9f3401cc..2539ee17 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Warum benötigt diese App Internetzugriff? diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 8c504e92..a904ebb9 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -56,9 +56,11 @@ Εξαγωγή μηνυμάτων Export SMS Export MMS + Check at least one of Export SMS or Export MMS Εισαγωγή μηνυμάτων Import SMS Import MMS + Check at least one of Import SMS or Import MMS Γιατί η εφαρμογή απαιτεί πρόσβαση στο Internet; diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index af192274..16b9a7a8 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS ¿Por qué la aplicación requiere acceso a internet? diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 858006f1..14fcf8cd 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Miksi sovellus vaatii Internet-yhteyden? diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9d3d90e2..9ac64dff 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -56,9 +56,11 @@ Export de messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import de messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Pourquoi cette application a besoin d\'un accès à internet ? diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index f2df0f2a..5244741c 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Por que o aplicativo necesita acceder a Internet? diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 6aeb25a1..573702c5 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Mengapa aplikasi membutuhkan akses ke internet? diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 6224ec79..f19c6920 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Perché l\'applicazione richiede l\'accesso ad internet? diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index b0bd9d9e..9a3c8982 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS なぜアプリ使用にインターネットへのアクセスが必要なのですか? diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index df287350..79e79648 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Why does the app require access to the internet? diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 826b61e6..4334c9b8 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS അപ്ലിക്കേഷന് ഇന്റർനെറ്റിലേക്ക് ആവശ്യമായി വരുന്നത് എന്തുകൊണ്ട്? diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 6ccbd17b..acec882f 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -56,9 +56,11 @@ Berichten exporteren Export SMS Export MMS + Check at least one of Export SMS or Export MMS Berichten importeren Import SMS Import MMS + Check at least one of Import SMS or Import MMS Waarom heeft deze app toegang nodig tot het internet? diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 3cd8ac2b..10120f86 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -58,9 +58,11 @@ Eksportuj wiadomości Export SMS Export MMS + Check at least one of Export SMS or Export MMS Importuj wiadomości Import SMS Import MMS + Check at least one of Import SMS or Import MMS Dlaczego aplikacja wymaga dostępu do Internetu? diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 6e463c16..59a8e881 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -56,9 +56,11 @@ Exportar mensagens Export SMS Export MMS + Check at least one of Export SMS or Export MMS Importar mensagens Import SMS Import MMS + Check at least one of Import SMS or Import MMS Porque é que a aplicação necessita de aceder à Internet? diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 59e27e03..3e9b2bab 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -58,9 +58,11 @@ Экспорт сообщений Export SMS Export MMS + Check at least one of Export SMS or Export MMS Импорт сообщений Import SMS Import MMS + Check at least one of Import SMS or Import MMS Почему приложение требует доступ к интернету? diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 877146e6..e47530c1 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -58,9 +58,11 @@ Exportovať správy Exportovať SMS Exportovať MMS + Check at least one of Export SMS or Export MMS Importovať správy Importovať SMS Importovať MMS + Check at least one of Import SMS or Import MMS Prečo vyžaduje apka prístup na internet? diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index ae654fd9..491e5350 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Uygulama neden internete erişim gerektiriyor? diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 96b32426..81b42c07 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS Чому додаток потрубує доступу до інтернету? diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 2baa5eff..c5161790 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -56,9 +56,11 @@ Export messages Export SMS Export MMS + Check at least one of Export SMS or Export MMS Import messages Import SMS Import MMS + Check at least one of Import SMS or Import MMS 为什么该应用需要访问互联网? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a4ac6ad8..23e6e9f5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -59,6 +59,8 @@ Import messages Import SMS Import MMS + Check at least one of Export SMS or Export MMS + Check at least one of Import SMS or Import MMS Why does the app require access to the internet? From de57cc3085000ec9c9c7eab0f020b160f5579bee Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Wed, 22 Sep 2021 22:55:31 +0200 Subject: [PATCH 24/29] minor code cleanup --- .../smsmessenger/helpers/MessagesImporter.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 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 167b191c..4ff1f5b7 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/MessagesImporter.kt @@ -1,12 +1,9 @@ package com.simplemobiletools.smsmessenger.helpers import android.content.Context -import android.net.Uri import android.provider.Telephony.* -import android.util.Log import com.google.gson.Gson import com.google.gson.reflect.TypeToken -import com.simplemobiletools.commons.extensions.queryCursor import com.simplemobiletools.commons.extensions.showErrorToast import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.smsmessenger.extensions.* @@ -44,6 +41,7 @@ class MessagesImporter(private val context: Context) { callback.invoke(IMPORT_NOTHING_NEW) return@ensureBackgroundThread } + onProgress.invoke(totalMessages, messagesImported) for (message in messages) { if (config.importSms) { @@ -70,9 +68,7 @@ class MessagesImporter(private val context: Context) { callback.invoke( when { - messagesImported == 0 -> { - IMPORT_FAIL - } + messagesImported == 0 -> IMPORT_FAIL messagesFailed > 0 -> IMPORT_PARTIAL else -> IMPORT_OK } From cbd6e7fa607b7ba64098e9073901a368d8a56690 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Wed, 22 Sep 2021 22:56:14 +0200 Subject: [PATCH 25/29] prevent importing the messages twice by confirming the dialog --- .../smsmessenger/dialogs/ImportMessagesDialog.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 f968498f..8766b659 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -12,8 +12,7 @@ import com.simplemobiletools.smsmessenger.helpers.MessagesImporter import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_FAIL import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_OK import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_PARTIAL -import kotlinx.android.synthetic.main.dialog_import_messages.view.import_mms_checkbox -import kotlinx.android.synthetic.main.dialog_import_messages.view.import_sms_checkbox +import kotlinx.android.synthetic.main.dialog_import_messages.view.* class ImportMessagesDialog( private val activity: SimpleActivity, @@ -24,6 +23,7 @@ class ImportMessagesDialog( private val config = activity.config init { + var ignoreClicks = false val view = (activity.layoutInflater.inflate(R.layout.dialog_import_messages, null) as ViewGroup).apply { import_sms_checkbox.isChecked = config.importSms import_mms_checkbox.isChecked = config.importMms @@ -35,11 +35,16 @@ class ImportMessagesDialog( .create().apply { activity.setupDialogStuff(view, this, R.string.import_messages) { getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - if(!view.import_sms_checkbox.isChecked && !view.import_mms_checkbox.isChecked){ + if (ignoreClicks) { + return@setOnClickListener + } + + if (!view.import_sms_checkbox.isChecked && !view.import_mms_checkbox.isChecked) { activity.toast(R.string.import_unchecked_error_message) return@setOnClickListener } + ignoreClicks = true activity.toast(R.string.importing) config.importSms = view.import_sms_checkbox.isChecked config.importMms = view.import_mms_checkbox.isChecked From ec2352e597c933becd30aa64376b845d3c1e6357 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Wed, 22 Sep 2021 22:59:51 +0200 Subject: [PATCH 26/29] minor code update --- .../smsmessenger/activities/MainActivity.kt | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) 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 2339ed69..24e3fd14 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -29,14 +29,13 @@ import com.simplemobiletools.smsmessenger.helpers.THREAD_ID import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE import com.simplemobiletools.smsmessenger.models.Conversation import com.simplemobiletools.smsmessenger.models.Events -import java.io.FileOutputStream -import java.io.OutputStream -import java.util.ArrayList -import java.util.Arrays 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 +import java.util.* class MainActivity : SimpleActivity() { private val MAKE_DEFAULT_APP_REQUEST = 1 @@ -340,7 +339,6 @@ class MainActivity : SimpleActivity() { 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) @@ -362,15 +360,15 @@ class MainActivity : SimpleActivity() { } private fun exportMessagesTo(outputStream: OutputStream?) { + toast(R.string.exporting) ensureBackgroundThread { - toast(R.string.exporting) smsExporter.exportMessages(outputStream) { - toast( - when (it) { - MessagesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful - else -> R.string.exporting_failed - } - ) + val toastId = when (it) { + MessagesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful + else -> R.string.exporting_failed + } + + toast(toastId) } } } From ae870aa04879c95a3516b1bcb0c8f793fdc58052 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Wed, 22 Sep 2021 23:02:48 +0200 Subject: [PATCH 27/29] adding some start margin to the export filename label --- app/src/main/res/layout/dialog_export_messages.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/dialog_export_messages.xml b/app/src/main/res/layout/dialog_export_messages.xml index 510967dd..66376d8e 100644 --- a/app/src/main/res/layout/dialog_export_messages.xml +++ b/app/src/main/res/layout/dialog_export_messages.xml @@ -34,6 +34,7 @@ android:id="@+id/export_messages_filename_label" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginStart="@dimen/small_margin" android:text="@string/filename_without_json" android:textSize="@dimen/smaller_text_size" /> From 24c08613d928f2580cd6f9494cd894585d6c6691 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Fri, 24 Sep 2021 15:07:40 +0100 Subject: [PATCH 28/29] remove unused callback in ImportMessagesDialog --- .../smsmessenger/activities/MainActivity.kt | 8 +------- .../smsmessenger/dialogs/ImportMessagesDialog.kt | 5 +---- 2 files changed, 2 insertions(+), 11 deletions(-) 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 2339ed69..15289689 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -398,13 +398,7 @@ class MainActivity : SimpleActivity() { } private fun showImportEventsDialog(path: String) { - ImportMessagesDialog(this, path) { refresh -> - if (refresh) { - runOnUiThread { - - } - } - } + ImportMessagesDialog(this, path) } private fun tryImportMessagesFromFile(uri: Uri) { 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 f968498f..29d3ce75 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/dialogs/ImportMessagesDialog.kt @@ -9,7 +9,6 @@ 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_FAIL import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_OK import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_PARTIAL import kotlinx.android.synthetic.main.dialog_import_messages.view.import_mms_checkbox @@ -18,7 +17,6 @@ import kotlinx.android.synthetic.main.dialog_import_messages.view.import_sms_che class ImportMessagesDialog( private val activity: SimpleActivity, private val path: String, - private val callback: (refresh: Boolean) -> Unit, ) { private val config = activity.config @@ -35,7 +33,7 @@ class ImportMessagesDialog( .create().apply { activity.setupDialogStuff(view, this, R.string.import_messages) { getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - if(!view.import_sms_checkbox.isChecked && !view.import_mms_checkbox.isChecked){ + if (!view.import_sms_checkbox.isChecked && !view.import_mms_checkbox.isChecked) { activity.toast(R.string.import_unchecked_error_message) return@setOnClickListener } @@ -62,6 +60,5 @@ class ImportMessagesDialog( else -> R.string.no_items_found } ) - callback(result != IMPORT_FAIL) } } From 24bc3931aa66ebd491cd90b8bc4e2922286fb0a9 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Fri, 24 Sep 2021 18:27:09 +0200 Subject: [PATCH 29/29] Update strings.xml --- app/src/main/res/values-sk/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index e47530c1..76e9b39c 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -58,11 +58,11 @@ Exportovať správy Exportovať SMS Exportovať MMS - Check at least one of Export SMS or Export MMS + Označte buď exportovanie SMS alebo exportovanie MMS Importovať správy Importovať SMS Importovať MMS - Check at least one of Import SMS or Import MMS + Označte buď importovanie SMS alebo importovanie MMS Prečo vyžaduje apka prístup na internet?