diff --git a/app/src/main/kotlin/org/fossify/messages/activities/SettingsActivity.kt b/app/src/main/kotlin/org/fossify/messages/activities/SettingsActivity.kt index aaefde8b..1e0dd20d 100644 --- a/app/src/main/kotlin/org/fossify/messages/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/org/fossify/messages/activities/SettingsActivity.kt @@ -1,12 +1,8 @@ package org.fossify.messages.activities import android.content.Intent -import android.net.Uri import android.os.Bundle -import android.provider.DocumentsContract import androidx.activity.result.contract.ActivityResultContracts -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.encodeToStream import org.fossify.commons.activities.ManageBlockedNumbersActivity import org.fossify.commons.dialogs.ChangeDateTimeFormatDialog import org.fossify.commons.dialogs.ConfirmationDialog @@ -24,7 +20,6 @@ import org.fossify.commons.extensions.getFontSizeText import org.fossify.commons.extensions.getProperPrimaryColor import org.fossify.commons.extensions.isOrWasThankYouInstalled import org.fossify.commons.extensions.launchPurchaseThankYouIntent -import org.fossify.commons.extensions.showErrorToast import org.fossify.commons.extensions.toast import org.fossify.commons.extensions.updateTextColors import org.fossify.commons.extensions.viewBinding @@ -56,7 +51,6 @@ import org.fossify.messages.helpers.LOCK_SCREEN_NOTHING import org.fossify.messages.helpers.LOCK_SCREEN_SENDER import org.fossify.messages.helpers.LOCK_SCREEN_SENDER_MESSAGE import org.fossify.messages.helpers.MessagesImporter -import org.fossify.messages.helpers.MessagesReader import org.fossify.messages.helpers.refreshMessages import java.util.Locale import kotlin.system.exitProcess @@ -81,11 +75,13 @@ class SettingsActivity : SimpleActivity() { } } + private var exportMessagesDialog: ExportMessagesDialog? = null + private val saveDocument = registerForActivityResult(ActivityResultContracts.CreateDocument(messagesFileType)) { uri -> if (uri != null) { toast(org.fossify.commons.R.string.exporting) - exportMessages(uri) + exportMessagesDialog?.exportMessages(uri) } } @@ -157,7 +153,7 @@ class SettingsActivity : SimpleActivity() { private fun setupMessagesExport() { binding.settingsExportMessagesHolder.setOnClickListener { - ExportMessagesDialog(this) { fileName -> + exportMessagesDialog = ExportMessagesDialog(this) { fileName -> saveDocument.launch("$fileName.json") } } @@ -169,41 +165,6 @@ class SettingsActivity : SimpleActivity() { } } - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) - private fun exportMessages(uri: Uri) { - ensureBackgroundThread { - var success = false - try { - MessagesReader(this).getMessagesToExport( - config.exportSms, - config.exportMms - ) { messagesToExport -> - if (messagesToExport.isEmpty()) { - toast(org.fossify.commons.R.string.no_entries_for_exporting) - return@getMessagesToExport - } - val json = Json { encodeDefaults = true } - contentResolver.openOutputStream(uri)!!.buffered().use { outputStream -> - json.encodeToStream(messagesToExport, outputStream) - } - success = true - toast(org.fossify.commons.R.string.exporting_successful) - } - } catch (e: Throwable) { // also catch OutOfMemoryError etc. - showErrorToast(e.toString()) - } finally { - if (!success) { - // delete the file to avoid leaving behind an empty/corrupt file - try { - DocumentsContract.deleteDocument(contentResolver, uri) - } catch (ignored: Exception) { - // ignored because we don't want to overwhelm the user with two error messages - } - } - } - } - } - override fun onPause() { super.onPause() blockedNumbersAtPause = getBlockedNumbers().hashCode() diff --git a/app/src/main/kotlin/org/fossify/messages/dialogs/ExportMessagesDialog.kt b/app/src/main/kotlin/org/fossify/messages/dialogs/ExportMessagesDialog.kt index e10c87d3..762a8b7e 100644 --- a/app/src/main/kotlin/org/fossify/messages/dialogs/ExportMessagesDialog.kt +++ b/app/src/main/kotlin/org/fossify/messages/dialogs/ExportMessagesDialog.kt @@ -1,47 +1,126 @@ package org.fossify.messages.dialogs +import android.annotation.SuppressLint +import android.net.Uri +import android.provider.DocumentsContract import androidx.appcompat.app.AlertDialog -import org.fossify.commons.extensions.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToStream +import org.fossify.commons.extensions.getAlertDialogBuilder +import org.fossify.commons.extensions.getCurrentFormattedDateTime +import org.fossify.commons.extensions.getProperPrimaryColor +import org.fossify.commons.extensions.isAValidFilename +import org.fossify.commons.extensions.setupDialogStuff +import org.fossify.commons.extensions.showErrorToast +import org.fossify.commons.extensions.toast +import org.fossify.commons.extensions.value +import org.fossify.commons.helpers.ensureBackgroundThread import org.fossify.messages.R import org.fossify.messages.activities.SimpleActivity import org.fossify.messages.databinding.DialogExportMessagesBinding import org.fossify.messages.extensions.config +import org.fossify.messages.helpers.MessagesReader class ExportMessagesDialog( private val activity: SimpleActivity, private val callback: (fileName: String) -> Unit, ) { private val config = activity.config + private var dialog: AlertDialog? = null + + @SuppressLint("SetTextI18n") + private val binding = DialogExportMessagesBinding.inflate(activity.layoutInflater).apply { + exportSmsCheckbox.isChecked = config.exportSms + exportMmsCheckbox.isChecked = config.exportMms + exportMessagesFilename.setText( + "${activity.getString(R.string.messages)}_${activity.getCurrentFormattedDateTime()}" + ) + } init { - val binding = DialogExportMessagesBinding.inflate(activity.layoutInflater).apply { - exportSmsCheckbox.isChecked = config.exportSms - exportMmsCheckbox.isChecked = config.exportMms - exportMessagesFilename.setText( - activity.getString(R.string.messages) + "_" + activity.getCurrentFormattedDateTime() - ) - } - activity.getAlertDialogBuilder() .setPositiveButton(org.fossify.commons.R.string.ok, null) .setNegativeButton(org.fossify.commons.R.string.cancel, null) .apply { - activity.setupDialogStuff(binding.root, this, R.string.export_messages) { alertDialog -> + activity.setupDialogStuff( + view = binding.root, + dialog = this, + titleId = R.string.export_messages + ) { alertDialog -> + dialog = alertDialog alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { config.exportSms = binding.exportSmsCheckbox.isChecked config.exportMms = binding.exportMmsCheckbox.isChecked val filename = binding.exportMessagesFilename.value when { filename.isEmpty() -> activity.toast(org.fossify.commons.R.string.empty_name) - filename.isAValidFilename() -> { - callback(filename) - alertDialog.dismiss() - } - + filename.isAValidFilename() -> callback(filename) else -> activity.toast(org.fossify.commons.R.string.invalid_name) } } } } } + + fun exportMessages(uri: Uri) { + dialog!!.apply { + setCanceledOnTouchOutside(false) + arrayOf( + binding.exportMmsCheckbox, + binding.exportSmsCheckbox, + getButton(AlertDialog.BUTTON_POSITIVE), + getButton(AlertDialog.BUTTON_NEGATIVE) + ).forEach { + it.isEnabled = false + it.alpha = 0.6f + } + + binding.exportProgress.setIndicatorColor(activity.getProperPrimaryColor()) + binding.exportProgress.post { + binding.exportProgress.show() + } + export(uri) + } + } + + @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + private fun export(uri: Uri) { + ensureBackgroundThread { + var success = false + try { + MessagesReader(activity).getMessagesToExport( + getSms = config.exportSms, + getMms = config.exportMms + ) { messagesToExport -> + if (messagesToExport.isEmpty()) { + activity.toast(org.fossify.commons.R.string.no_entries_for_exporting) + dismiss() + return@getMessagesToExport + } + val json = Json { encodeDefaults = true } + activity.contentResolver.openOutputStream(uri)!!.buffered() + .use { outputStream -> + json.encodeToStream(messagesToExport, outputStream) + } + success = true + activity.toast(org.fossify.commons.R.string.exporting_successful) + } + } catch (e: Throwable) { + activity.showErrorToast(e.toString()) + } finally { + if (!success) { + // delete the file to avoid leaving behind an empty/corrupt file + try { + DocumentsContract.deleteDocument(activity.contentResolver, uri) + } catch (ignored: Exception) { + // ignored because we don't want to show two error messages + } + } + + dismiss() + } + } + } + + private fun dismiss() = dialog?.dismiss() } diff --git a/app/src/main/kotlin/org/fossify/messages/dialogs/ImportMessagesDialog.kt b/app/src/main/kotlin/org/fossify/messages/dialogs/ImportMessagesDialog.kt index 750c1b68..da99019a 100644 --- a/app/src/main/kotlin/org/fossify/messages/dialogs/ImportMessagesDialog.kt +++ b/app/src/main/kotlin/org/fossify/messages/dialogs/ImportMessagesDialog.kt @@ -2,6 +2,7 @@ package org.fossify.messages.dialogs import androidx.appcompat.app.AlertDialog import org.fossify.commons.extensions.getAlertDialogBuilder +import org.fossify.commons.extensions.getProperPrimaryColor import org.fossify.commons.extensions.setupDialogStuff import org.fossify.commons.extensions.toast import org.fossify.commons.helpers.ensureBackgroundThread @@ -27,12 +28,20 @@ class ImportMessagesDialog( importMmsCheckbox.isChecked = config.importMms } + binding.importProgress.setIndicatorColor(activity.getProperPrimaryColor()) + activity.getAlertDialogBuilder() .setPositiveButton(org.fossify.commons.R.string.ok, null) .setNegativeButton(org.fossify.commons.R.string.cancel, null) .apply { - activity.setupDialogStuff(binding.root, this, R.string.import_messages) { alertDialog -> - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + activity.setupDialogStuff( + view = binding.root, + dialog = this, + titleId = R.string.import_messages + ) { alertDialog -> + val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) + val negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) + positiveButton.setOnClickListener { if (ignoreClicks) { return@setOnClickListener } @@ -46,6 +55,19 @@ class ImportMessagesDialog( activity.toast(org.fossify.commons.R.string.importing) config.importSms = binding.importSmsCheckbox.isChecked config.importMms = binding.importMmsCheckbox.isChecked + + alertDialog.setCanceledOnTouchOutside(false) + binding.importProgress.show() + arrayOf( + binding.importMmsCheckbox, + binding.importSmsCheckbox, + positiveButton, + negativeButton + ).forEach { + it.isEnabled = false + it.alpha = 0.6f + } + ensureBackgroundThread { MessagesImporter(activity).restoreMessages(messages) { handleParseResult(it) diff --git a/app/src/main/res/layout/dialog_export_messages.xml b/app/src/main/res/layout/dialog_export_messages.xml index 693c4774..7038e96c 100644 --- a/app/src/main/res/layout/dialog_export_messages.xml +++ b/app/src/main/res/layout/dialog_export_messages.xml @@ -1,5 +1,6 @@ + android:paddingTop="@dimen/activity_margin"> + + diff --git a/app/src/main/res/layout/dialog_import_messages.xml b/app/src/main/res/layout/dialog_import_messages.xml index 666291b0..b480346f 100644 --- a/app/src/main/res/layout/dialog_import_messages.xml +++ b/app/src/main/res/layout/dialog_import_messages.xml @@ -1,5 +1,7 @@ @@ -9,14 +11,26 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingStart="@dimen/activity_margin" - android:paddingTop="@dimen/activity_margin" - android:paddingEnd="@dimen/activity_margin"> + android:paddingTop="@dimen/activity_margin"> + + @@ -25,6 +39,7 @@ android:id="@+id/import_mms_checkbox" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/activity_margin" android:paddingTop="@dimen/small_margin" android:paddingBottom="@dimen/small_margin" android:text="@string/import_mms" />