Merge branch 'master' into feature/451-recycle-bin
This commit is contained in:
commit
dbf582b239
33 changed files with 570 additions and 590 deletions
|
|
@ -3,19 +3,15 @@ package com.simplemobiletools.smsmessenger.activities
|
|||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.role.RoleManager
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.graphics.drawable.Icon
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Telephony
|
||||
import android.text.TextUtils
|
||||
import android.widget.Toast
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||
import com.simplemobiletools.commons.dialogs.PermissionRequiredDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
|
|
@ -25,8 +21,6 @@ import com.simplemobiletools.smsmessenger.BuildConfig
|
|||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.adapters.ConversationsAdapter
|
||||
import com.simplemobiletools.smsmessenger.adapters.SearchResultsAdapter
|
||||
import com.simplemobiletools.smsmessenger.dialogs.ExportMessagesDialog
|
||||
import com.simplemobiletools.smsmessenger.dialogs.ImportMessagesDialog
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
|
|
@ -37,19 +31,14 @@ import kotlinx.android.synthetic.main.activity_main.*
|
|||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import java.io.FileOutputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
class MainActivity : SimpleActivity() {
|
||||
private val MAKE_DEFAULT_APP_REQUEST = 1
|
||||
private val PICK_IMPORT_SOURCE_INTENT = 11
|
||||
private val PICK_EXPORT_FILE_INTENT = 21
|
||||
|
||||
private var storedTextColor = 0
|
||||
private var storedFontSize = 0
|
||||
private var lastSearchedText = ""
|
||||
private var bus: EventBus? = null
|
||||
private val smsExporter by lazy { MessagesExporter(this) }
|
||||
private var wasProtectionHandled = false
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
|
|
@ -176,8 +165,6 @@ class MainActivity : SimpleActivity() {
|
|||
|
||||
main_menu.getToolbar().setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.import_messages -> tryImportMessages()
|
||||
R.id.export_messages -> tryToExportMessages()
|
||||
R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
|
||||
R.id.show_recycle_bin -> launchRecycleBin()
|
||||
R.id.show_archived -> launchArchivedConversations()
|
||||
|
|
@ -204,11 +191,6 @@ class MainActivity : SimpleActivity() {
|
|||
} else {
|
||||
finish()
|
||||
}
|
||||
} else if (requestCode == PICK_IMPORT_SOURCE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) {
|
||||
tryImportMessagesFromFile(resultData.data!!)
|
||||
} else if (requestCode == PICK_EXPORT_FILE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) {
|
||||
val outputStream = contentResolver.openOutputStream(resultData.data!!)
|
||||
exportMessagesTo(outputStream)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -602,119 +584,6 @@ class MainActivity : SimpleActivity() {
|
|||
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
|
||||
}
|
||||
|
||||
private fun tryToExportMessages() {
|
||||
if (isQPlus()) {
|
||||
ExportMessagesDialog(this, config.lastExportPath, true) { file ->
|
||||
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
type = JSON_MIME_TYPE
|
||||
putExtra(Intent.EXTRA_TITLE, file.name)
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
|
||||
try {
|
||||
startActivityForResult(this, PICK_EXPORT_FILE_INTENT)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
toast(R.string.system_service_disabled, Toast.LENGTH_LONG)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) {
|
||||
if (it) {
|
||||
ExportMessagesDialog(this, config.lastExportPath, false) { file ->
|
||||
getFileOutputStream(file.toFileDirItem(this), true) { outStream ->
|
||||
exportMessagesTo(outStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportMessagesTo(outputStream: OutputStream?) {
|
||||
toast(R.string.exporting)
|
||||
ensureBackgroundThread {
|
||||
smsExporter.exportMessages(outputStream) {
|
||||
val toastId = when (it) {
|
||||
MessagesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful
|
||||
else -> R.string.exporting_failed
|
||||
}
|
||||
|
||||
toast(toastId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryImportMessages() {
|
||||
if (isQPlus()) {
|
||||
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = JSON_MIME_TYPE
|
||||
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(JSON_MIME_TYPE, XML_MIME_TYPE, TXT_MIME_TYPE))
|
||||
|
||||
try {
|
||||
startActivityForResult(this, PICK_IMPORT_SOURCE_INTENT)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
toast(R.string.system_service_disabled, Toast.LENGTH_LONG)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handlePermission(PERMISSION_READ_STORAGE) {
|
||||
if (it) {
|
||||
importMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun importMessages() {
|
||||
FilePickerDialog(this) {
|
||||
showImportMessagesDialog(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showImportMessagesDialog(path: String) {
|
||||
ImportMessagesDialog(this, path)
|
||||
}
|
||||
|
||||
private fun tryImportMessagesFromFile(uri: Uri) {
|
||||
when (uri.scheme) {
|
||||
"file" -> showImportMessagesDialog(uri.path!!)
|
||||
"content" -> {
|
||||
var 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)
|
||||
// Check is XML and properly rename
|
||||
tempFile.bufferedReader().use {
|
||||
if (it.readLine().startsWith("<?xml")) {
|
||||
val xmlFile = getTempFile("messages", "backup.xml")
|
||||
if (xmlFile == null || tempFile?.renameTo(xmlFile) == false) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return
|
||||
}
|
||||
tempFile = xmlFile
|
||||
}
|
||||
}
|
||||
showImportMessagesDialog(tempFile!!.absolutePath)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
|
||||
else -> toast(R.string.invalid_file_format)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun refreshMessages(event: Events.RefreshMessages) {
|
||||
initMessenger()
|
||||
|
|
|
|||
|
|
@ -2,24 +2,33 @@ package com.simplemobiletools.smsmessenger.activities
|
|||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import com.simplemobiletools.commons.activities.ManageBlockedNumbersActivity
|
||||
import com.simplemobiletools.commons.dialogs.*
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.dialogs.ExportMessagesDialog
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import com.simplemobiletools.smsmessenger.extensions.emptyMessagesRecycleBin
|
||||
import com.simplemobiletools.smsmessenger.extensions.messagesDB
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import kotlinx.android.synthetic.main.activity_settings.*
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.util.*
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class SettingsActivity : SimpleActivity() {
|
||||
private var blockedNumbersAtPause = -1
|
||||
private var recycleBinMessages = 0
|
||||
private val messagesFileType = "application/json"
|
||||
private val messageImportFileTypes = listOf("application/json", "application/xml", "text/xml")
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
isMaterialActivity = true
|
||||
|
|
@ -54,6 +63,8 @@ class SettingsActivity : SimpleActivity() {
|
|||
setupUseRecycleBin()
|
||||
setupEmptyRecycleBin()
|
||||
setupAppPasswordProtection()
|
||||
setupMessagesExport()
|
||||
setupMessagesImport()
|
||||
updateTextColors(settings_nested_scrollview)
|
||||
|
||||
if (blockedNumbersAtPause != -1 && blockedNumbersAtPause != getBlockedNumbers().hashCode()) {
|
||||
|
|
@ -66,12 +77,63 @@ class SettingsActivity : SimpleActivity() {
|
|||
settings_outgoing_messages_label,
|
||||
settings_notifications_label,
|
||||
settings_recycle_bin_label,
|
||||
settings_security_label
|
||||
settings_security_label,
|
||||
settings_migrating_label
|
||||
).forEach {
|
||||
it.setTextColor(getProperPrimaryColor())
|
||||
}
|
||||
}
|
||||
|
||||
private val getContent = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
||||
if (uri != null) {
|
||||
MessagesImporter(this).importMessages(uri)
|
||||
}
|
||||
}
|
||||
|
||||
private val saveDocument = registerForActivityResult(ActivityResultContracts.CreateDocument(messagesFileType)) { uri ->
|
||||
if (uri != null) {
|
||||
toast(R.string.exporting)
|
||||
exportMessages(uri)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMessagesExport() {
|
||||
settings_export_messages_holder.setOnClickListener {
|
||||
ExportMessagesDialog(this) { fileName ->
|
||||
saveDocument.launch(fileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMessagesImport() {
|
||||
settings_import_messages_holder.setOnClickListener {
|
||||
getContent.launch(messageImportFileTypes.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportMessages(uri: Uri) {
|
||||
ensureBackgroundThread {
|
||||
try {
|
||||
MessagesReader(this).getMessagesToExport(config.exportSms, config.exportMms) { messagesToExport ->
|
||||
if (messagesToExport.isEmpty()) {
|
||||
toast(R.string.no_entries_for_exporting)
|
||||
return@getMessagesToExport
|
||||
}
|
||||
val json = Json { encodeDefaults = true }
|
||||
val jsonString = json.encodeToString(messagesToExport)
|
||||
val outputStream = contentResolver.openOutputStream(uri)!!
|
||||
|
||||
outputStream.use {
|
||||
it.write(jsonString.toByteArray())
|
||||
}
|
||||
toast(R.string.exporting_successful)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
blockedNumbersAtPause = getBlockedNumbers().hashCode()
|
||||
|
|
@ -104,7 +166,7 @@ class SettingsActivity : SimpleActivity() {
|
|||
settings_use_english_holder.setOnClickListener {
|
||||
settings_use_english.toggle()
|
||||
config.useEnglish = settings_use_english.isChecked
|
||||
System.exit(0)
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,42 +2,27 @@ 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.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import com.simplemobiletools.smsmessenger.helpers.JSON_FILE_EXTENSION
|
||||
import kotlinx.android.synthetic.main.dialog_export_messages.view.*
|
||||
import java.io.File
|
||||
import kotlinx.android.synthetic.main.dialog_export_messages.view.export_messages_filename
|
||||
import kotlinx.android.synthetic.main.dialog_export_messages.view.export_mms_checkbox
|
||||
import kotlinx.android.synthetic.main.dialog_export_messages.view.export_sms_checkbox
|
||||
|
||||
class ExportMessagesDialog(
|
||||
private val activity: SimpleActivity,
|
||||
private val path: String,
|
||||
private val hidePath: Boolean,
|
||||
private val callback: (file: File) -> Unit,
|
||||
private val callback: (fileName: String) -> Unit,
|
||||
) {
|
||||
private var realPath = if (path.isEmpty()) activity.internalStoragePath else path
|
||||
private val config = activity.config
|
||||
|
||||
init {
|
||||
val view = (activity.layoutInflater.inflate(R.layout.dialog_export_messages, null) as ViewGroup).apply {
|
||||
export_messages_folder.setText(activity.humanizePath(realPath))
|
||||
export_messages_filename.setText("${activity.getString(R.string.messages)}_${activity.getCurrentFormattedDateTime()}")
|
||||
export_sms_checkbox.isChecked = config.exportSms
|
||||
export_mms_checkbox.isChecked = config.exportMms
|
||||
|
||||
if (hidePath) {
|
||||
export_messages_folder_hint.beGone()
|
||||
} else {
|
||||
export_messages_folder.setOnClickListener {
|
||||
activity.hideKeyboard(export_messages_filename)
|
||||
FilePickerDialog(activity, realPath, false, showFAB = true) {
|
||||
export_messages_folder.setText(activity.humanizePath(it))
|
||||
realPath = it
|
||||
}
|
||||
}
|
||||
}
|
||||
export_messages_filename.setText(
|
||||
activity.getString(R.string.messages) + "_" + activity.getCurrentFormattedDateTime()
|
||||
)
|
||||
}
|
||||
|
||||
activity.getAlertDialogBuilder()
|
||||
|
|
@ -45,29 +30,17 @@ class ExportMessagesDialog(
|
|||
.setNegativeButton(R.string.cancel, null)
|
||||
.apply {
|
||||
activity.setupDialogStuff(view, this, R.string.export_messages) { alertDialog ->
|
||||
alertDialog.showKeyboard(view.export_messages_filename)
|
||||
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
config.exportSms = view.export_sms_checkbox.isChecked
|
||||
config.exportMms = view.export_mms_checkbox.isChecked
|
||||
val filename = view.export_messages_filename.value
|
||||
when {
|
||||
filename.isEmpty() -> activity.toast(R.string.empty_name)
|
||||
filename.isAValidFilename() -> {
|
||||
val file = File(realPath, "$filename$JSON_FILE_EXTENSION")
|
||||
if (!hidePath && file.exists()) {
|
||||
activity.toast(R.string.name_taken)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (!view.export_sms_checkbox.isChecked && !view.export_mms_checkbox.isChecked) {
|
||||
activity.toast(R.string.no_option_selected)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
config.exportSms = view.export_sms_checkbox.isChecked
|
||||
config.exportMms = view.export_mms_checkbox.isChecked
|
||||
config.lastExportPath = file.absolutePath.getParentPath()
|
||||
callback(file)
|
||||
callback(filename)
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
|
||||
else -> activity.toast(R.string.invalid_name)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ import com.simplemobiletools.smsmessenger.R
|
|||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter
|
||||
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_OK
|
||||
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_PARTIAL
|
||||
import com.simplemobiletools.smsmessenger.models.MessagesBackup
|
||||
import com.simplemobiletools.smsmessenger.models.ImportResult
|
||||
import kotlinx.android.synthetic.main.dialog_import_messages.view.*
|
||||
|
||||
class ImportMessagesDialog(
|
||||
private val activity: SimpleActivity,
|
||||
private val path: String,
|
||||
private val messages: List<MessagesBackup>,
|
||||
) {
|
||||
|
||||
private val config = activity.config
|
||||
|
|
@ -48,7 +48,7 @@ class ImportMessagesDialog(
|
|||
config.importSms = view.import_sms_checkbox.isChecked
|
||||
config.importMms = view.import_mms_checkbox.isChecked
|
||||
ensureBackgroundThread {
|
||||
MessagesImporter(activity).importMessages(path) {
|
||||
MessagesImporter(activity).restoreMessages(messages) {
|
||||
handleParseResult(it)
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
|
|
@ -58,11 +58,12 @@ class ImportMessagesDialog(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleParseResult(result: MessagesImporter.ImportResult) {
|
||||
private fun handleParseResult(result: ImportResult) {
|
||||
activity.toast(
|
||||
when (result) {
|
||||
IMPORT_OK -> R.string.importing_successful
|
||||
IMPORT_PARTIAL -> R.string.importing_some_entries_failed
|
||||
ImportResult.IMPORT_OK -> R.string.importing_successful
|
||||
ImportResult.IMPORT_PARTIAL -> R.string.importing_some_entries_failed
|
||||
ImportResult.IMPORT_FAIL -> R.string.importing_failed
|
||||
else -> R.string.no_items_found
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import com.simplemobiletools.smsmessenger.extensions.getConversationIds
|
||||
import java.io.OutputStream
|
||||
|
||||
class MessagesExporter(private val context: Context) {
|
||||
enum class ExportResult {
|
||||
EXPORT_FAIL, EXPORT_OK
|
||||
}
|
||||
|
||||
private val config = context.config
|
||||
private val messageReader = MessagesReader(context)
|
||||
private val gson = Gson()
|
||||
|
||||
fun exportMessages(outputStream: OutputStream?, onProgress: (total: Int, current: Int) -> Unit = { _, _ -> }, callback: (result: ExportResult) -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
if (outputStream == null) {
|
||||
callback.invoke(ExportResult.EXPORT_FAIL)
|
||||
return@ensureBackgroundThread
|
||||
}
|
||||
val writer = JsonWriter(outputStream.bufferedWriter())
|
||||
writer.use {
|
||||
try {
|
||||
var written = 0
|
||||
writer.beginArray()
|
||||
val conversationIds = context.getConversationIds()
|
||||
val totalMessages = messageReader.getMessagesCount()
|
||||
for (threadId in conversationIds) {
|
||||
writer.beginObject()
|
||||
if (config.exportSms && messageReader.getSmsCount() > 0) {
|
||||
writer.name("sms")
|
||||
writer.beginArray()
|
||||
messageReader.forEachSms(threadId) {
|
||||
writer.jsonValue(gson.toJson(it))
|
||||
written++
|
||||
onProgress.invoke(totalMessages, written)
|
||||
}
|
||||
writer.endArray()
|
||||
}
|
||||
|
||||
if (config.exportMms && messageReader.getMmsCount() > 0) {
|
||||
writer.name("mms")
|
||||
writer.beginArray()
|
||||
messageReader.forEachMms(threadId) {
|
||||
writer.jsonValue(gson.toJson(it))
|
||||
written++
|
||||
onProgress.invoke(totalMessages, written)
|
||||
}
|
||||
|
||||
writer.endArray()
|
||||
}
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
writer.endArray()
|
||||
callback.invoke(ExportResult.EXPORT_OK)
|
||||
} catch (e: Exception) {
|
||||
callback.invoke(ExportResult.EXPORT_FAIL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,172 +1,145 @@
|
|||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.util.JsonToken
|
||||
import android.net.Uri
|
||||
import android.util.Xml
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.dialogs.ImportMessagesDialog
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_FAIL
|
||||
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_NOTHING_NEW
|
||||
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_OK
|
||||
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_PARTIAL
|
||||
import com.simplemobiletools.smsmessenger.models.MmsBackup
|
||||
import com.simplemobiletools.smsmessenger.models.SmsBackup
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
class MessagesImporter(private val context: Context) {
|
||||
enum class ImportResult {
|
||||
IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL, IMPORT_NOTHING_NEW
|
||||
}
|
||||
|
||||
private val gson = Gson()
|
||||
private val messageWriter = MessagesWriter(context)
|
||||
private val config = context.config
|
||||
class MessagesImporter(private val activity: SimpleActivity) {
|
||||
|
||||
private val messageWriter = MessagesWriter(activity)
|
||||
private val config = activity.config
|
||||
private var messagesImported = 0
|
||||
private var messagesFailed = 0
|
||||
|
||||
fun importMessages(path: String, onProgress: (total: Int, current: Int) -> Unit = { _, _ -> }, callback: (result: ImportResult) -> Unit) {
|
||||
fun importMessages(uri: Uri) {
|
||||
try {
|
||||
val fileType = activity.contentResolver.getType(uri).orEmpty()
|
||||
val isXml = isXmlMimeType(fileType) || (uri.path?.endsWith("txt") == true && isFileXml(uri))
|
||||
if (isXml) {
|
||||
activity.toast(R.string.importing)
|
||||
getInputStreamFromUri(uri)!!.importXml()
|
||||
} else {
|
||||
importJson(uri)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun importJson(uri: Uri) {
|
||||
try {
|
||||
val jsonString = activity.contentResolver.openInputStream(uri)!!.use { inputStream ->
|
||||
inputStream.bufferedReader().readText()
|
||||
}
|
||||
|
||||
val deserializedList = Json.decodeFromString<List<MessagesBackup>>(jsonString)
|
||||
if (deserializedList.isEmpty()) {
|
||||
activity.toast(R.string.no_entries_for_importing)
|
||||
return
|
||||
}
|
||||
ImportMessagesDialog(activity, deserializedList)
|
||||
} catch (e: SerializationException) {
|
||||
activity.toast(R.string.invalid_file_format)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
activity.toast(R.string.invalid_file_format)
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreMessages(messagesBackup: List<MessagesBackup>, callback: (ImportResult) -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
try {
|
||||
val isXml = if (path.endsWith("txt")) {
|
||||
// Need to read the first line to determine if it is xml
|
||||
val tempStream = getInputStreamForPath(path)
|
||||
tempStream.bufferedReader().use {
|
||||
it.readLine().startsWith("<?xml")
|
||||
messagesBackup.forEach { message ->
|
||||
try {
|
||||
if (message.backupType == BackupType.SMS && config.importSms) {
|
||||
messageWriter.writeSmsMessage(message as SmsBackup)
|
||||
messagesImported++
|
||||
} else if (message.backupType == BackupType.MMS && config.importMms) {
|
||||
messageWriter.writeMmsMessage(message as MmsBackup)
|
||||
messagesImported++
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
messagesFailed++
|
||||
}
|
||||
} else {
|
||||
path.endsWith("xml")
|
||||
}
|
||||
|
||||
val inputStream = getInputStreamForPath(path)
|
||||
|
||||
if (isXml) {
|
||||
inputStream.importXml()
|
||||
} else {
|
||||
inputStream.importJson()
|
||||
}
|
||||
|
||||
refreshMessages()
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
messagesFailed++
|
||||
activity.showErrorToast(e)
|
||||
}
|
||||
|
||||
callback.invoke(
|
||||
when {
|
||||
messagesImported == 0 && messagesFailed == 0 -> IMPORT_NOTHING_NEW
|
||||
messagesFailed > 0 && messagesImported > 0 -> IMPORT_PARTIAL
|
||||
messagesFailed > 0 -> IMPORT_FAIL
|
||||
else -> IMPORT_OK
|
||||
messagesImported == 0 && messagesFailed == 0 -> ImportResult.IMPORT_NOTHING_NEW
|
||||
messagesFailed > 0 && messagesImported > 0 -> ImportResult.IMPORT_PARTIAL
|
||||
messagesFailed > 0 -> ImportResult.IMPORT_FAIL
|
||||
else -> ImportResult.IMPORT_OK
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInputStreamForPath(path: String): InputStream {
|
||||
return if (path.contains("/")) {
|
||||
File(path).inputStream()
|
||||
} else {
|
||||
context.assets.open(path)
|
||||
}
|
||||
}
|
||||
|
||||
private fun InputStream.importJson() {
|
||||
bufferedReader().use { reader ->
|
||||
val jsonReader = gson.newJsonReader(reader)
|
||||
val smsMessageType = object : TypeToken<SmsBackup>() {}.type
|
||||
val mmsMessageType = object : TypeToken<MmsBackup>() {}.type
|
||||
|
||||
jsonReader.beginArray()
|
||||
while (jsonReader.hasNext()) {
|
||||
jsonReader.beginObject()
|
||||
while (jsonReader.hasNext()) {
|
||||
val nextToken = jsonReader.peek()
|
||||
if (nextToken.ordinal == JsonToken.NAME.ordinal) {
|
||||
val msgType = jsonReader.nextName()
|
||||
|
||||
if ((!msgType.equals("sms") && !msgType.equals("mms")) ||
|
||||
(msgType.equals("sms") && !config.importSms) ||
|
||||
(msgType.equals("mms") && !config.importMms)
|
||||
) {
|
||||
jsonReader.skipValue()
|
||||
continue
|
||||
}
|
||||
|
||||
jsonReader.beginArray()
|
||||
while (jsonReader.hasNext()) {
|
||||
try {
|
||||
if (msgType.equals("sms")) {
|
||||
val message = gson.fromJson<SmsBackup>(jsonReader, smsMessageType)
|
||||
messageWriter.writeSmsMessage(message)
|
||||
} else {
|
||||
val message = gson.fromJson<MmsBackup>(jsonReader, mmsMessageType)
|
||||
messageWriter.writeMmsMessage(message)
|
||||
}
|
||||
|
||||
messagesImported++
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
messagesFailed++
|
||||
|
||||
}
|
||||
}
|
||||
jsonReader.endArray()
|
||||
} else {
|
||||
jsonReader.skipValue()
|
||||
}
|
||||
}
|
||||
|
||||
jsonReader.endObject()
|
||||
refreshMessages()
|
||||
}
|
||||
|
||||
jsonReader.endArray()
|
||||
}
|
||||
}
|
||||
|
||||
private fun InputStream.importXml() {
|
||||
bufferedReader().use { reader ->
|
||||
val xmlParser = Xml.newPullParser().apply {
|
||||
setInput(reader)
|
||||
}
|
||||
|
||||
xmlParser.nextTag()
|
||||
xmlParser.require(XmlPullParser.START_TAG, null, "smses")
|
||||
|
||||
var depth = 1
|
||||
while (depth != 0) {
|
||||
when (xmlParser.next()) {
|
||||
XmlPullParser.END_TAG -> depth--
|
||||
XmlPullParser.START_TAG -> depth++
|
||||
try {
|
||||
bufferedReader().use { reader ->
|
||||
val xmlParser = Xml.newPullParser().apply {
|
||||
setInput(reader)
|
||||
}
|
||||
|
||||
if (xmlParser.eventType != XmlPullParser.START_TAG) {
|
||||
continue
|
||||
}
|
||||
xmlParser.nextTag()
|
||||
xmlParser.require(XmlPullParser.START_TAG, null, "smses")
|
||||
|
||||
try {
|
||||
if (xmlParser.name == "sms") {
|
||||
if (config.importSms) {
|
||||
val message = xmlParser.readSms()
|
||||
messageWriter.writeSmsMessage(message)
|
||||
messagesImported++
|
||||
var depth = 1
|
||||
while (depth != 0) {
|
||||
when (xmlParser.next()) {
|
||||
XmlPullParser.END_TAG -> depth--
|
||||
XmlPullParser.START_TAG -> depth++
|
||||
}
|
||||
|
||||
if (xmlParser.eventType != XmlPullParser.START_TAG) {
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
if (xmlParser.name == "sms") {
|
||||
if (config.importSms) {
|
||||
val message = xmlParser.readSms()
|
||||
messageWriter.writeSmsMessage(message)
|
||||
messagesImported++
|
||||
} else {
|
||||
xmlParser.skip()
|
||||
}
|
||||
} else {
|
||||
xmlParser.skip()
|
||||
}
|
||||
} else {
|
||||
xmlParser.skip()
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
messagesFailed++
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
messagesFailed++
|
||||
}
|
||||
refreshMessages()
|
||||
}
|
||||
|
||||
refreshMessages()
|
||||
when {
|
||||
messagesFailed > 0 && messagesImported > 0 -> activity.toast(R.string.importing_some_entries_failed)
|
||||
messagesFailed > 0 -> activity.toast(R.string.importing_failed)
|
||||
else -> activity.toast(R.string.importing_successful)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
activity.toast(R.string.invalid_file_format)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,4 +173,27 @@ class MessagesImporter(private val context: Context) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInputStreamFromUri(uri: Uri): InputStream? {
|
||||
return try {
|
||||
activity.contentResolver.openInputStream(uri)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun isFileXml(uri: Uri): Boolean {
|
||||
val inputStream = getInputStreamFromUri(uri)
|
||||
return inputStream?.bufferedReader()?.use { reader ->
|
||||
reader.readLine()?.startsWith("<?xml") ?: false
|
||||
} ?: false
|
||||
}
|
||||
|
||||
private fun isXmlMimeType(mimeType: String): Boolean {
|
||||
return mimeType.equals("application/xml", ignoreCase = true) || mimeType.equals("text/xml", ignoreCase = true)
|
||||
}
|
||||
|
||||
private fun isJsonMimeType(mimeType: String): Boolean {
|
||||
return mimeType.equals("application/json", ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,30 @@ import android.util.Base64
|
|||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.isQPlus
|
||||
import com.simplemobiletools.commons.helpers.isRPlus
|
||||
import com.simplemobiletools.smsmessenger.models.MmsAddress
|
||||
import com.simplemobiletools.smsmessenger.models.MmsBackup
|
||||
import com.simplemobiletools.smsmessenger.models.MmsPart
|
||||
import com.simplemobiletools.smsmessenger.models.SmsBackup
|
||||
import com.simplemobiletools.smsmessenger.extensions.getConversationIds
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
class MessagesReader(private val context: Context) {
|
||||
fun forEachSms(threadId: Long, block: (SmsBackup) -> Unit) {
|
||||
|
||||
fun getMessagesToExport(
|
||||
getSms: Boolean, getMms: Boolean, callback: (messages: List<MessagesBackup>) -> Unit
|
||||
) {
|
||||
val conversationIds = context.getConversationIds()
|
||||
var smsMessages = listOf<SmsBackup>()
|
||||
var mmsMessages = listOf<MmsBackup>()
|
||||
|
||||
if (getSms) {
|
||||
smsMessages = getSmsMessages(conversationIds)
|
||||
}
|
||||
if (getMms) {
|
||||
mmsMessages = getMmsMessages(conversationIds)
|
||||
}
|
||||
callback(smsMessages + mmsMessages)
|
||||
}
|
||||
|
||||
private fun getSmsMessages(threadIds: List<Long>): List<SmsBackup> {
|
||||
val projection = arrayOf(
|
||||
Sms.SUBSCRIPTION_ID,
|
||||
Sms.ADDRESS,
|
||||
|
|
@ -33,25 +48,28 @@ class MessagesReader(private val context: Context) {
|
|||
)
|
||||
|
||||
val selection = "${Sms.THREAD_ID} = ?"
|
||||
val selectionArgs = arrayOf(threadId.toString())
|
||||
context.queryCursor(Sms.CONTENT_URI, projection, selection, selectionArgs) { cursor ->
|
||||
val subscriptionId = cursor.getLongValue(Sms.SUBSCRIPTION_ID)
|
||||
val address = cursor.getStringValue(Sms.ADDRESS)
|
||||
val body = cursor.getStringValueOrNull(Sms.BODY)
|
||||
val date = cursor.getLongValue(Sms.DATE)
|
||||
val dateSent = cursor.getLongValue(Sms.DATE_SENT)
|
||||
val locked = cursor.getIntValue(Sms.LOCKED)
|
||||
val protocol = cursor.getStringValueOrNull(Sms.PROTOCOL)
|
||||
val read = cursor.getIntValue(Sms.READ)
|
||||
val status = cursor.getIntValue(Sms.STATUS)
|
||||
val type = cursor.getIntValue(Sms.TYPE)
|
||||
val serviceCenter = cursor.getStringValueOrNull(Sms.SERVICE_CENTER)
|
||||
block(SmsBackup(subscriptionId, address, body, date, dateSent, locked, protocol, read, status, type, serviceCenter))
|
||||
val smsList = mutableListOf<SmsBackup>()
|
||||
|
||||
threadIds.map { it.toString() }.forEach { threadId ->
|
||||
context.queryCursor(Sms.CONTENT_URI, projection, selection, arrayOf(threadId)) { cursor ->
|
||||
val subscriptionId = cursor.getLongValue(Sms.SUBSCRIPTION_ID)
|
||||
val address = cursor.getStringValue(Sms.ADDRESS)
|
||||
val body = cursor.getStringValueOrNull(Sms.BODY)
|
||||
val date = cursor.getLongValue(Sms.DATE)
|
||||
val dateSent = cursor.getLongValue(Sms.DATE_SENT)
|
||||
val locked = cursor.getIntValue(Sms.DATE_SENT)
|
||||
val protocol = cursor.getStringValueOrNull(Sms.PROTOCOL)
|
||||
val read = cursor.getIntValue(Sms.READ)
|
||||
val status = cursor.getIntValue(Sms.STATUS)
|
||||
val type = cursor.getIntValue(Sms.TYPE)
|
||||
val serviceCenter = cursor.getStringValueOrNull(Sms.SERVICE_CENTER)
|
||||
smsList.add(SmsBackup(subscriptionId, address, body, date, dateSent, locked, protocol, read, status, type, serviceCenter))
|
||||
}
|
||||
}
|
||||
return smsList
|
||||
}
|
||||
|
||||
// all mms from simple sms are non-text messages
|
||||
fun forEachMms(threadId: Long, includeTextOnlyAttachment: Boolean = false, block: (MmsBackup) -> Unit) {
|
||||
private fun getMmsMessages(threadIds: List<Long>, includeTextOnlyAttachment: Boolean = false): List<MmsBackup> {
|
||||
val projection = arrayOf(
|
||||
Mms._ID,
|
||||
Mms.CREATOR,
|
||||
|
|
@ -71,65 +89,67 @@ class MessagesReader(private val context: Context) {
|
|||
Mms.SUBSCRIPTION_ID,
|
||||
Mms.TRANSACTION_ID
|
||||
)
|
||||
|
||||
val selection = if (includeTextOnlyAttachment) {
|
||||
"${Mms.THREAD_ID} = ? AND ${Mms.TEXT_ONLY} = ?"
|
||||
} else {
|
||||
"${Mms.THREAD_ID} = ?"
|
||||
}
|
||||
val mmsList = mutableListOf<MmsBackup>()
|
||||
|
||||
val selectionArgs = if (includeTextOnlyAttachment) {
|
||||
arrayOf(threadId.toString(), "1")
|
||||
} else {
|
||||
arrayOf(threadId.toString())
|
||||
}
|
||||
threadIds.map { it.toString() }.forEach { threadId ->
|
||||
val selectionArgs = if (includeTextOnlyAttachment) {
|
||||
arrayOf(threadId, "1")
|
||||
} else {
|
||||
arrayOf(threadId)
|
||||
}
|
||||
context.queryCursor(Mms.CONTENT_URI, projection, selection, selectionArgs) { cursor ->
|
||||
val mmsId = cursor.getLongValue(Mms._ID)
|
||||
val creator = cursor.getStringValueOrNull(Mms.CREATOR)
|
||||
val contentType = cursor.getStringValueOrNull(Mms.CONTENT_TYPE)
|
||||
val deliveryReport = cursor.getIntValue(Mms.DELIVERY_REPORT)
|
||||
val date = cursor.getLongValue(Mms.DATE)
|
||||
val dateSent = cursor.getLongValue(Mms.DATE_SENT)
|
||||
val locked = cursor.getIntValue(Mms.LOCKED)
|
||||
val messageType = cursor.getIntValue(Mms.MESSAGE_TYPE)
|
||||
val messageBox = cursor.getIntValue(Mms.MESSAGE_BOX)
|
||||
val read = cursor.getIntValue(Mms.READ)
|
||||
val readReport = cursor.getIntValue(Mms.READ_REPORT)
|
||||
val seen = cursor.getIntValue(Mms.SEEN)
|
||||
val textOnly = cursor.getIntValue(Mms.TEXT_ONLY)
|
||||
val status = cursor.getStringValueOrNull(Mms.STATUS)
|
||||
val subject = cursor.getStringValueOrNull(Mms.SUBJECT)
|
||||
val subjectCharSet = cursor.getStringValueOrNull(Mms.SUBJECT_CHARSET)
|
||||
val subscriptionId = cursor.getLongValue(Mms.SUBSCRIPTION_ID)
|
||||
val transactionId = cursor.getStringValueOrNull(Mms.TRANSACTION_ID)
|
||||
|
||||
context.queryCursor(Mms.CONTENT_URI, projection, selection, selectionArgs) { cursor ->
|
||||
val mmsId = cursor.getLongValue(Mms._ID)
|
||||
val creator = cursor.getStringValueOrNull(Mms.CREATOR)
|
||||
val contentType = cursor.getStringValueOrNull(Mms.CONTENT_TYPE)
|
||||
val deliveryReport = cursor.getIntValue(Mms.DELIVERY_REPORT)
|
||||
val date = cursor.getLongValue(Mms.DATE)
|
||||
val dateSent = cursor.getLongValue(Mms.DATE_SENT)
|
||||
val locked = cursor.getIntValue(Mms.LOCKED)
|
||||
val messageType = cursor.getIntValue(Mms.MESSAGE_TYPE)
|
||||
val messageBox = cursor.getIntValue(Mms.MESSAGE_BOX)
|
||||
val read = cursor.getIntValue(Mms.READ)
|
||||
val readReport = cursor.getIntValue(Mms.READ_REPORT)
|
||||
val seen = cursor.getIntValue(Mms.SEEN)
|
||||
val textOnly = cursor.getIntValue(Mms.TEXT_ONLY)
|
||||
val status = cursor.getStringValueOrNull(Mms.STATUS)
|
||||
val subject = cursor.getStringValueOrNull(Mms.SUBJECT)
|
||||
val subjectCharSet = cursor.getStringValueOrNull(Mms.SUBJECT_CHARSET)
|
||||
val subscriptionId = cursor.getLongValue(Mms.SUBSCRIPTION_ID)
|
||||
val transactionId = cursor.getStringValueOrNull(Mms.TRANSACTION_ID)
|
||||
|
||||
val parts = getParts(mmsId)
|
||||
val addresses = getMmsAddresses(mmsId)
|
||||
block(
|
||||
MmsBackup(
|
||||
creator,
|
||||
contentType,
|
||||
deliveryReport,
|
||||
date,
|
||||
dateSent,
|
||||
locked,
|
||||
messageType,
|
||||
messageBox,
|
||||
read,
|
||||
readReport,
|
||||
seen,
|
||||
textOnly,
|
||||
status,
|
||||
subject,
|
||||
subjectCharSet,
|
||||
subscriptionId,
|
||||
transactionId,
|
||||
addresses,
|
||||
parts
|
||||
val parts = getParts(mmsId)
|
||||
val addresses = getMmsAddresses(mmsId)
|
||||
mmsList.add(
|
||||
MmsBackup(
|
||||
creator,
|
||||
contentType,
|
||||
deliveryReport,
|
||||
date,
|
||||
dateSent,
|
||||
locked,
|
||||
messageType,
|
||||
messageBox,
|
||||
read,
|
||||
readReport,
|
||||
seen,
|
||||
textOnly,
|
||||
status,
|
||||
subject,
|
||||
subjectCharSet,
|
||||
subscriptionId,
|
||||
transactionId,
|
||||
addresses,
|
||||
parts
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
return mmsList
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class BackupType {
|
||||
@SerialName("sms")
|
||||
SMS,
|
||||
|
||||
@SerialName("mms")
|
||||
MMS,
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
enum class ImportResult {
|
||||
IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL, IMPORT_NOTHING_NEW
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
@Serializable(with = BackupSerializer::class)
|
||||
sealed class MessagesBackup() {
|
||||
@SerialName("backupType")
|
||||
abstract val backupType: BackupType
|
||||
}
|
||||
|
||||
object BackupSerializer :
|
||||
JsonContentPolymorphicSerializer<MessagesBackup>(MessagesBackup::class) {
|
||||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out MessagesBackup> {
|
||||
return when (element.jsonObject["backupType"]?.jsonPrimitive?.content) {
|
||||
"sms" -> SmsBackup.serializer()
|
||||
"mms" -> MmsBackup.serializer()
|
||||
else -> throw SerializationException("ERROR: No Serializer found. Serialization failed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,9 @@ import android.content.ContentValues
|
|||
import android.provider.Telephony
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MmsAddress(
|
||||
@SerializedName("address")
|
||||
val address: String,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import android.content.ContentValues
|
|||
import android.provider.Telephony
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MmsBackup(
|
||||
@SerializedName("creator")
|
||||
val creator: String?,
|
||||
|
|
@ -44,7 +46,9 @@ data class MmsBackup(
|
|||
val addresses: List<MmsAddress>,
|
||||
@SerializedName("parts")
|
||||
val parts: List<MmsPart>,
|
||||
) {
|
||||
|
||||
override val backupType: BackupType = BackupType.MMS,
|
||||
): MessagesBackup() {
|
||||
|
||||
fun toContentValues(): ContentValues {
|
||||
return contentValuesOf(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import android.content.ContentValues
|
|||
import android.provider.Telephony
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MmsPart(
|
||||
@SerializedName("cd")
|
||||
val contentDisposition: String?,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import android.content.ContentValues
|
|||
import android.provider.Telephony
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SmsBackup(
|
||||
@SerializedName("sub_id")
|
||||
val subscriptionId: Long,
|
||||
|
|
@ -28,8 +30,10 @@ data class SmsBackup(
|
|||
@SerializedName("type")
|
||||
val type: Int,
|
||||
@SerializedName("service_center")
|
||||
val serviceCenter: String?
|
||||
) {
|
||||
val serviceCenter: String?,
|
||||
|
||||
override val backupType: BackupType = BackupType.SMS,
|
||||
): MessagesBackup() {
|
||||
|
||||
fun toContentValues(): ContentValues {
|
||||
return contentValuesOf(
|
||||
|
|
|
|||
|
|
@ -431,6 +431,47 @@
|
|||
android:text="@string/password_protect_whole_app" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/settings_migrating_divider"
|
||||
layout="@layout/divider" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings_migrating_label"
|
||||
style="@style/SettingsSectionLabelStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/migrating" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/settings_export_messages_holder"
|
||||
style="@style/SettingsHolderTextViewOneLinerStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/settings_export_messages"
|
||||
style="@style/SettingsTextLabelStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/export_messages" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/settings_import_messages_holder"
|
||||
style="@style/SettingsHolderTextViewOneLinerStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/settings_import_messages"
|
||||
style="@style/SettingsTextLabelStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/import_messages" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
|
|||
|
|
@ -14,21 +14,6 @@
|
|||
android:paddingTop="@dimen/activity_margin"
|
||||
android:paddingEnd="@dimen/activity_margin">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextInputLayout
|
||||
android:id="@+id/export_messages_folder_hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:hint="@string/folder">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/export_messages_folder"
|
||||
style="@style/UnclickableEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</com.simplemobiletools.commons.views.MyTextInputLayout>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextInputLayout
|
||||
android:id="@+id/export_messages_filename_hint"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -3,16 +3,6 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="AppCompatResource,AlwaysShowAction">
|
||||
<item
|
||||
android:id="@+id/import_messages"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/import_messages"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/export_messages"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/export_messages"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/show_recycle_bin"
|
||||
android:showAsAction="never"
|
||||
|
|
|
|||
|
|
@ -63,14 +63,14 @@
|
|||
<string name="mark_as_unread">وضع علامة كغير مقروءة</string>
|
||||
<string name="me">Me</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
<string name="unarchive">إلغاء الأرشفة</string>
|
||||
<string name="empty_archive">حذف جميع المحادثات المؤرشفة</string>
|
||||
<string name="archived_conversations">أرشيف</string>
|
||||
<string name="show_archived_conversations">إظهار المحادثات المؤرشفة</string>
|
||||
<string name="archive">أرشيف</string>
|
||||
<string name="no_archived_conversations">لم يتم العثور على محادثات مؤرشفة</string>
|
||||
<string name="archive_emptied_successfully">تم إفراغ الأرشيف بنجاح</string>
|
||||
<string name="empty_archive_confirmation">هل أنت متأكد من أنك تريد إفراغ الأرشيف؟ ستفقد جميع المحادثات المؤرشفة نهائيا.</string>
|
||||
<!-- Recycle bin -->
|
||||
<string name="restore">Restore</string>
|
||||
<string name="restore_all_messages">Restore all messages</string>
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
<string name="skip_the_recycle_bin_messages">Skip the Recycle Bin, delete messages directly</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">هل أنت متأكد أنك تريد حذف كافة رسائل هذه المحادثة؟</string>
|
||||
<string name="archive_confirmation">Are you sure you want to archive %s?</string>
|
||||
<string name="archive_confirmation">هل أنت متأكد من أنك تريد أرشفة %s؟</string>
|
||||
<string name="restore_whole_conversation_confirmation">Are you sure you want to restore all messages of this conversation?</string>
|
||||
<string name="restore_confirmation">Are you sure you want to restore %s?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@
|
|||
<string name="update_message">Nachricht aktualisieren</string>
|
||||
<string name="send_now">Jetzt senden</string>
|
||||
<!-- Message details -->
|
||||
<string name="message_details">Message details</string>
|
||||
<string name="message_details_sender">Sender</string>
|
||||
<string name="message_details_receiver">Receiver</string>
|
||||
<string name="message_details_sent_at">Sent at</string>
|
||||
<string name="message_details_received_at">Received at</string>
|
||||
<string name="message_details">Nachrichtendetails</string>
|
||||
<string name="message_details_sender">Absender</string>
|
||||
<string name="message_details_receiver">Empfänger</string>
|
||||
<string name="message_details_sent_at">Gesendet am</string>
|
||||
<string name="message_details_received_at">Empfangen am</string>
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Empfangene SMS</string>
|
||||
<string name="new_message">Neue Nachricht</string>
|
||||
|
|
@ -59,14 +59,14 @@
|
|||
<string name="mark_as_unread">Als ungelesen markieren</string>
|
||||
<string name="me">Ich</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
<string name="unarchive">Dearchivieren</string>
|
||||
<string name="empty_archive">Alle archivierten Unterhaltungen löschen</string>
|
||||
<string name="archived_conversations">Archiv</string>
|
||||
<string name="show_archived_conversations">Archivierte Unterhaltungen anzeigen</string>
|
||||
<string name="archive">Archivieren</string>
|
||||
<string name="no_archived_conversations">Es wurden keine archivierten Unterhaltungen gefunden</string>
|
||||
<string name="archive_emptied_successfully">Das Archiv wurde erfolgreich geleert</string>
|
||||
<string name="empty_archive_confirmation">Das Archiv wirklich leeren\? Alle archivierten Unterhaltungen sind dann unwiederbringlich gelöscht.</string>
|
||||
<!-- Recycle bin -->
|
||||
<string name="restore">Restore</string>
|
||||
<string name="restore_all_messages">Restore all messages</string>
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
<string name="skip_the_recycle_bin_messages">Skip the Recycle Bin, delete messages directly</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Sollen wirklich alle Nachrichten dieser Unterhaltung gelöscht werden\?</string>
|
||||
<string name="archive_confirmation">Are you sure you want to archive %s?</string>
|
||||
<string name="archive_confirmation">%s wirklich archivieren\?</string>
|
||||
<string name="restore_whole_conversation_confirmation">Are you sure you want to restore all messages of this conversation?</string>
|
||||
<string name="restore_confirmation">Are you sure you want to restore %s?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
|
|
@ -88,11 +88,11 @@
|
|||
<item quantity="other">%d Nachrichten</item>
|
||||
</plurals>
|
||||
<!-- Settings -->
|
||||
<string name="keyword">Keyword</string>
|
||||
<string name="blocked_keywords">Blocked keywords</string>
|
||||
<string name="manage_blocked_keywords">Manage blocked keywords</string>
|
||||
<string name="not_blocking_keywords">You are not blocking any keywords. You may add keywords here to block all messages containing them.</string>
|
||||
<string name="add_a_blocked_keyword">Add a blocked keyword</string>
|
||||
<string name="keyword">Schlüsselwort</string>
|
||||
<string name="blocked_keywords">Blockierte Schlüsselwörter</string>
|
||||
<string name="manage_blocked_keywords">Blockierte Schlüsselwörter verwalten</string>
|
||||
<string name="not_blocking_keywords">Du hast keine Schlüsselwörter blockiert. Du kannst hier Schlüsselwörter hinzufügen, um alle Nachrichten zu blockieren, die sie enthalten.</string>
|
||||
<string name="add_a_blocked_keyword">Ein zu blockierendes Schlüsselwort hinzufügen</string>
|
||||
<string name="lock_screen_visibility">Sichtbarkeit von Benachrichtigungen auf dem Sperrbildschirm</string>
|
||||
<string name="sender_and_message">Absender und Nachricht</string>
|
||||
<string name="sender_only">Nur Absender</string>
|
||||
|
|
|
|||
|
|
@ -59,14 +59,14 @@
|
|||
<string name="mark_as_unread">Σήμανση ως Μη Αναγνωσμένο</string>
|
||||
<string name="me">Εγώ</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
<string name="unarchive">Μη αρχειοθέτηση</string>
|
||||
<string name="empty_archive">Διαγραφή όλων των αρχειοθετημένων συνομιλιών</string>
|
||||
<string name="archived_conversations">Αρχειοθέτηση</string>
|
||||
<string name="show_archived_conversations">Εμφάνιση αρχειοθετημένων συνομιλιών</string>
|
||||
<string name="archive">Αρχειοθέτηση</string>
|
||||
<string name="no_archived_conversations">Δεν βρέθηκαν αρχειοθετημένες συνομιλίες</string>
|
||||
<string name="archive_emptied_successfully">Το αρχείο άδειασε με επιτυχία</string>
|
||||
<string name="empty_archive_confirmation">Είστε σίγουροι ότι θέλετε να αδειάσετε το αρχείο; Όλες οι αρχειοθετημένες συνομιλίες θα χαθούν οριστικά.</string>
|
||||
<!-- Recycle bin -->
|
||||
<string name="restore">Restore</string>
|
||||
<string name="restore_all_messages">Restore all messages</string>
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
<string name="skip_the_recycle_bin_messages">Skip the Recycle Bin, delete messages directly</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Είστε βέβαιοι ότι θέλετε να διαγράψετε όλα τα μηνύματα αυτής της συνομιλίας;</string>
|
||||
<string name="archive_confirmation">Are you sure you want to archive %s?</string>
|
||||
<string name="archive_confirmation">Σίγουρα θέλετε να αρχειοθετήσετε %s;</string>
|
||||
<string name="restore_whole_conversation_confirmation">Are you sure you want to restore all messages of this conversation?</string>
|
||||
<string name="restore_confirmation">Are you sure you want to restore %s?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Lihtne SMS Sõnumitooja</string>
|
||||
<string name="app_launcher_name">SMS Sõnumitooja</string>
|
||||
<string name="app_name">Lihtne SMS sõnumiklient</string>
|
||||
<string name="app_launcher_name">SMS sõnumid</string>
|
||||
<string name="type_a_message">Kirjuta sõnum…</string>
|
||||
<string name="message_not_sent_short">Sõnum ei ole saadetud</string>
|
||||
<string name="message_not_sent_touch_retry">Saamata. Uuesti saatmiseks puuduta.</string>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<string name="no_reply_support">Sõnumi saatja ei toeta vastuste võimalust</string>
|
||||
<string name="draft">Kavand</string>
|
||||
<string name="sending">Saadame…</string>
|
||||
<string name="pin_conversation">Kinnitage ülaossa</string>
|
||||
<string name="pin_conversation">Kinnita ülaossa</string>
|
||||
<string name="unpin_conversation">Eemalda kinnitus</string>
|
||||
<string name="forward_message">Edasta</string>
|
||||
<string name="compress_error">Pildi muutmine valitud suurusesse ei õnnestu</string>
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">ja %d muud</item>
|
||||
<item quantity="other">ja %d teised</item>
|
||||
<item quantity="other">ja %d muud</item>
|
||||
</plurals>
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Uus vestlus</string>
|
||||
|
|
@ -48,10 +48,10 @@
|
|||
<string name="send_now">Saada kohe</string>
|
||||
<!-- Message details -->
|
||||
<string name="message_details">Message details</string>
|
||||
<string name="message_details_sender">Sender</string>
|
||||
<string name="message_details_receiver">Receiver</string>
|
||||
<string name="message_details_sent_at">Sent at</string>
|
||||
<string name="message_details_received_at">Received at</string>
|
||||
<string name="message_details_sender">Saatja</string>
|
||||
<string name="message_details_receiver">Saaja</string>
|
||||
<string name="message_details_sent_at">Saatmise aeg</string>
|
||||
<string name="message_details_received_at">Saabumise aeg</string>
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Vastuvõetud SMS</string>
|
||||
<string name="new_message">Uus sõnum</string>
|
||||
|
|
@ -59,14 +59,14 @@
|
|||
<string name="mark_as_unread">Märgi mitteloetuks</string>
|
||||
<string name="me">Mina</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
<string name="unarchive">Eemalda arhiivist</string>
|
||||
<string name="empty_archive">Kustuta kõik arhiveeritud vestlused</string>
|
||||
<string name="archived_conversations">Vestluste arhiiv</string>
|
||||
<string name="show_archived_conversations">Näita arhiveeritud vestlusi</string>
|
||||
<string name="archive">Arhiiv</string>
|
||||
<string name="no_archived_conversations">Arhiveeritud vestlusi ei leidu</string>
|
||||
<string name="archive_emptied_successfully">Arhiiv on edukalt tühjendatud</string>
|
||||
<string name="empty_archive_confirmation">Kas kindlasti soovid arhiivi tühjendada\? Kõik arhiveeritud vestlused lähevad jäädavalt kaotsi.</string>
|
||||
<!-- Recycle bin -->
|
||||
<string name="restore">Restore</string>
|
||||
<string name="restore_all_messages">Restore all messages</string>
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
<string name="skip_the_recycle_bin_messages">Skip the Recycle Bin, delete messages directly</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Kas oled kindel, et soovid kustutada kõik selle vestluse sõnumid\?</string>
|
||||
<string name="archive_confirmation">Are you sure you want to archive %s?</string>
|
||||
<string name="archive_confirmation">Kas sa oled kindel, et soovid lisada „%s“ arhiivi\?</string>
|
||||
<string name="restore_whole_conversation_confirmation">Are you sure you want to restore all messages of this conversation?</string>
|
||||
<string name="restore_confirmation">Are you sure you want to restore %s?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
|
|
@ -85,14 +85,14 @@
|
|||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d sõnum</item>
|
||||
<item quantity="other">%d sõnumeid</item>
|
||||
<item quantity="other">%d sõnumit</item>
|
||||
</plurals>
|
||||
<!-- Settings -->
|
||||
<string name="keyword">Keyword</string>
|
||||
<string name="blocked_keywords">Blocked keywords</string>
|
||||
<string name="manage_blocked_keywords">Manage blocked keywords</string>
|
||||
<string name="not_blocking_keywords">You are not blocking any keywords. You may add keywords here to block all messages containing them.</string>
|
||||
<string name="add_a_blocked_keyword">Add a blocked keyword</string>
|
||||
<string name="keyword">Märksõna</string>
|
||||
<string name="blocked_keywords">Blokeeritud märksõnad</string>
|
||||
<string name="manage_blocked_keywords">Halda blokeeritud märksõnu</string>
|
||||
<string name="not_blocking_keywords">Sa hetkel ei blokeeri ühtegi märksõna. Võid siia lisada märksõnu, et blokeerida kõik neid sisaldavad sõnumid.</string>
|
||||
<string name="add_a_blocked_keyword">Lisa blokeeritud märksõna</string>
|
||||
<string name="lock_screen_visibility">Teavituse nähtavus lukustusvaates</string>
|
||||
<string name="sender_and_message">Saatja ja sõnum</string>
|
||||
<string name="sender_only">Ainult saatja</string>
|
||||
|
|
@ -103,15 +103,15 @@
|
|||
<string name="mms_file_size_limit_none">Piirangut ei ole</string>
|
||||
<string name="outgoing_messages">Väljuvad sõnumid</string>
|
||||
<string name="group_message_mms">Saada rühmasõnumid MMS-sõnumitena</string>
|
||||
<string name="send_long_message_mms">Pikkade sõnumite saatmine MMS-ina</string>
|
||||
<string name="send_long_message_mms">Saada pikad sõnumid MMS-ina</string>
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Sõnumid</string>
|
||||
<string name="export_messages">Sõnumite eksportimine</string>
|
||||
<string name="export_sms">Ekspordi SMS</string>
|
||||
<string name="export_mms">Ekspordi MMS</string>
|
||||
<string name="import_messages">Sõnumite importimine</string>
|
||||
<string name="import_sms">Impordi SMS</string>
|
||||
<string name="import_mms">Impordi MMS</string>
|
||||
<string name="export_messages">Ekspordi sõnumid</string>
|
||||
<string name="export_sms">Ekspordi tekstisõnumid</string>
|
||||
<string name="export_mms">Ekspordi MMS-sõnumid</string>
|
||||
<string name="import_messages">Impordi sõnumid</string>
|
||||
<string name="import_sms">Impordi tekstisõnumid</string>
|
||||
<string name="import_mms">Impordi MMS-sõnumid</string>
|
||||
<string name="no_option_selected">Palun vali vähemalt üks kirje</string>
|
||||
<!-- Errors -->
|
||||
<string name="empty_destination_address">Kui number on sisestamata, siis sõnumit saata ei saa</string>
|
||||
|
|
|
|||
|
|
@ -59,14 +59,14 @@
|
|||
<string name="mark_as_unread">Als ongelezen markeren</string>
|
||||
<string name="me">Ik</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
<string name="unarchive">Herstellen</string>
|
||||
<string name="empty_archive">Alle gearchiveerde gesprekken verwijderen</string>
|
||||
<string name="archived_conversations">Archief</string>
|
||||
<string name="show_archived_conversations">Gearchiveerde gesprekken tonen</string>
|
||||
<string name="archive">Archiveren</string>
|
||||
<string name="no_archived_conversations">Geen gearchiveerde gesprekken gevonden</string>
|
||||
<string name="archive_emptied_successfully">Archief is gewist</string>
|
||||
<string name="empty_archive_confirmation">Het archief wissen\? Alle gearchiveerde gesprekken zullen permanent verwijderd worden.</string>
|
||||
<!-- Recycle bin -->
|
||||
<string name="restore">Restore</string>
|
||||
<string name="restore_all_messages">Restore all messages</string>
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
<string name="skip_the_recycle_bin_messages">Skip the Recycle Bin, delete messages directly</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Alle berichten in dit gesprek verwijderen\?</string>
|
||||
<string name="archive_confirmation">Are you sure you want to archive %s?</string>
|
||||
<string name="archive_confirmation">%s archiveren\?</string>
|
||||
<string name="restore_whole_conversation_confirmation">Are you sure you want to restore all messages of this conversation?</string>
|
||||
<string name="restore_confirmation">Are you sure you want to restore %s?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
|
|
|
|||
|
|
@ -61,14 +61,14 @@
|
|||
<string name="mark_as_unread">Oznacz jako nieprzeczytane</string>
|
||||
<string name="me">Ja</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
<string name="unarchive">Cofnij archiwizację</string>
|
||||
<string name="empty_archive">Usuń wszystkie zarchiwizowane rozmowy</string>
|
||||
<string name="archived_conversations">Archiwum</string>
|
||||
<string name="show_archived_conversations">Pokaż zarchiwizowane rozmowy</string>
|
||||
<string name="archive">Zarchiwizuj</string>
|
||||
<string name="no_archived_conversations">Nie znaleziono zarchiwizowanych rozmów</string>
|
||||
<string name="archive_emptied_successfully">Archiwum zostało opróżnione</string>
|
||||
<string name="empty_archive_confirmation">Czy opróżnić archiwum\? Wszystkie zarchiwizowane rozmowy zostaną utracone bezpowrotnie.</string>
|
||||
<!-- Recycle bin -->
|
||||
<string name="restore">Restore</string>
|
||||
<string name="restore_all_messages">Restore all messages</string>
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
<string name="skip_the_recycle_bin_messages">Skip the Recycle Bin, delete messages directly</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Czy usunąć wszystkie wiadomości z tej rozmowy\?</string>
|
||||
<string name="archive_confirmation">Are you sure you want to archive %s?</string>
|
||||
<string name="archive_confirmation">Czy zarchiwizować %s\?</string>
|
||||
<string name="restore_whole_conversation_confirmation">Are you sure you want to restore all messages of this conversation?</string>
|
||||
<string name="restore_confirmation">Are you sure you want to restore %s?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
|
|
|
|||
|
|
@ -61,11 +61,11 @@
|
|||
<string name="mark_as_unread">Не прочитано</string>
|
||||
<string name="me">Я</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="unarchive">Разархивировать</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="archive">Архивировать</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
|
|
@ -141,4 +141,4 @@
|
|||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
@ -59,14 +59,14 @@
|
|||
<string name="mark_as_unread">Okunmadı olarak işaretle</string>
|
||||
<string name="me">Ben</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
<string name="unarchive">Arşivden çıkar</string>
|
||||
<string name="empty_archive">Arşivlenen tüm görüşmeleri sil</string>
|
||||
<string name="archived_conversations">Arşiv</string>
|
||||
<string name="show_archived_conversations">Arşivlenen görüşmeleri göster</string>
|
||||
<string name="archive">Arşiv</string>
|
||||
<string name="no_archived_conversations">Arşivlenen görüşme bulunamadı</string>
|
||||
<string name="archive_emptied_successfully">Arşiv başarıyla boşaltıldı</string>
|
||||
<string name="empty_archive_confirmation">Arşivi boşaltmak istediğinizden emin misiniz\? Arşivlenen tüm görüşmeler kalıcı olarak kaybolacak.</string>
|
||||
<!-- Recycle bin -->
|
||||
<string name="restore">Restore</string>
|
||||
<string name="restore_all_messages">Restore all messages</string>
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
<string name="skip_the_recycle_bin_messages">Skip the Recycle Bin, delete messages directly</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Bu görüşmenin tüm mesajlarını silmek istediğinizden emin misiniz\?</string>
|
||||
<string name="archive_confirmation">Are you sure you want to archive %s?</string>
|
||||
<string name="archive_confirmation">%s arşivlemek istediğinizden emin misiniz\?</string>
|
||||
<string name="restore_whole_conversation_confirmation">Are you sure you want to restore all messages of this conversation?</string>
|
||||
<string name="restore_confirmation">Are you sure you want to restore %s?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
|
|
|
|||
|
|
@ -58,14 +58,14 @@
|
|||
<string name="mark_as_unread">标记为未读</string>
|
||||
<string name="me">自己</string>
|
||||
<!-- Archive -->
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="empty_archive">Delete all archived conversations</string>
|
||||
<string name="archived_conversations">Archive</string>
|
||||
<string name="show_archived_conversations">Show archived conversations</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="no_archived_conversations">No archived conversations have been found</string>
|
||||
<string name="archive_emptied_successfully">The archive has been emptied successfully</string>
|
||||
<string name="empty_archive_confirmation">Are you sure you want to empty the archive? All archived conversations will be permanently lost.</string>
|
||||
<string name="unarchive">解压缩</string>
|
||||
<string name="empty_archive">删除所有已归档对话</string>
|
||||
<string name="archived_conversations">归档</string>
|
||||
<string name="show_archived_conversations">显示已归档对话</string>
|
||||
<string name="archive">归档</string>
|
||||
<string name="no_archived_conversations">尚未找到已归档对话</string>
|
||||
<string name="archive_emptied_successfully">已成功清空归档</string>
|
||||
<string name="empty_archive_confirmation">你确定要清空归档吗?所有已归档对话将永久丢失。</string>
|
||||
<!-- Recycle bin -->
|
||||
<string name="restore">Restore</string>
|
||||
<string name="restore_all_messages">Restore all messages</string>
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
<string name="skip_the_recycle_bin_messages">Skip the Recycle Bin, delete messages directly</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">您确定要删除此对话的所有消息吗\?</string>
|
||||
<string name="archive_confirmation">Are you sure you want to archive %s?</string>
|
||||
<string name="archive_confirmation">你确定要归档 %s 吗?</string>
|
||||
<string name="restore_whole_conversation_confirmation">Are you sure you want to restore all messages of this conversation?</string>
|
||||
<string name="restore_confirmation">Are you sure you want to restore %s?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue