mirror of
https://github.com/SimpleMobileTools/Simple-SMS-Messenger.git
synced 2025-02-17 04:00:35 +01:00
Merge branch 'master' into fix/wrong-sender-name
This commit is contained in:
commit
7fae2d8324
@ -56,7 +56,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:307e9e2e82'
|
||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:d4b6547969'
|
||||
implementation 'org.greenrobot:eventbus:3.2.0'
|
||||
implementation 'com.klinkerapps:android-smsmms:5.2.6'
|
||||
implementation 'com.github.tibbi:IndicatorFastScroll:c3de1d040a'
|
||||
|
@ -8,17 +8,23 @@ 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
|
||||
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.MessagesExporter
|
||||
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
|
||||
import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
@ -27,14 +33,20 @@ 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.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
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 bus: EventBus? = null
|
||||
private val smsExporter by lazy { MessagesExporter(this) }
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -108,6 +120,8 @@ class MainActivity : SimpleActivity() {
|
||||
when (item.itemId) {
|
||||
R.id.search -> launchSearch()
|
||||
R.id.settings -> launchSettings()
|
||||
R.id.export_messages -> tryToExportMessages()
|
||||
R.id.import_messages -> tryImportMessages()
|
||||
R.id.about -> launchAbout()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
@ -122,6 +136,11 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,7 +189,7 @@ class MainActivity : SimpleActivity() {
|
||||
private fun getCachedConversations() {
|
||||
ensureBackgroundThread {
|
||||
val conversations = try {
|
||||
conversationsDB.getAll().sortedByDescending { it.date }.toMutableList() as ArrayList<Conversation>
|
||||
conversationsDB.getAll().toMutableList() as ArrayList<Conversation>
|
||||
} catch (e: Exception) {
|
||||
ArrayList()
|
||||
}
|
||||
@ -226,6 +245,10 @@ class MainActivity : SimpleActivity() {
|
||||
|
||||
private fun setupConversations(conversations: ArrayList<Conversation>) {
|
||||
val hasConversations = conversations.isNotEmpty()
|
||||
val sortedConversations = conversations.sortedWith(
|
||||
compareByDescending<Conversation> { config.pinnedConversations.contains(it.threadId.toString()) }
|
||||
.thenByDescending { it.date }
|
||||
).toMutableList() as ArrayList<Conversation>
|
||||
conversations_list.beVisibleIf(hasConversations)
|
||||
no_conversations_placeholder.beVisibleIf(!hasConversations)
|
||||
no_conversations_placeholder_2.beVisibleIf(!hasConversations)
|
||||
@ -237,7 +260,7 @@ class MainActivity : SimpleActivity() {
|
||||
|
||||
val currAdapter = conversations_list.adapter
|
||||
if (currAdapter == null) {
|
||||
ConversationsAdapter(this, conversations, conversations_list, conversations_fastscroller) {
|
||||
ConversationsAdapter(this, sortedConversations, conversations_list, conversations_fastscroller) {
|
||||
Intent(this, ThreadActivity::class.java).apply {
|
||||
putExtra(THREAD_ID, (it as Conversation).threadId)
|
||||
putExtra(THREAD_TITLE, it.title)
|
||||
@ -254,7 +277,7 @@ class MainActivity : SimpleActivity() {
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
(currAdapter as ConversationsAdapter).updateConversations(conversations)
|
||||
(currAdapter as ConversationsAdapter).updateConversations(sortedConversations)
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
@ -318,6 +341,92 @@ 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?) {
|
||||
toast(R.string.exporting)
|
||||
ensureBackgroundThread {
|
||||
smsExporter.exportMessages(outputStream) {
|
||||
val toastId = when (it) {
|
||||
MessagesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful
|
||||
else -> R.string.exporting_failed
|
||||
}
|
||||
|
||||
toast(toastId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryImportMessages() {
|
||||
if (isQPlus()) {
|
||||
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = EXPORT_MIME_TYPE
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
|
@ -146,6 +146,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
}
|
||||
|
||||
updateMenuItemColors(menu)
|
||||
checkPinBtnVisibility(menu)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -159,6 +160,8 @@ class ThreadActivity : SimpleActivity() {
|
||||
R.id.delete -> askConfirmDelete()
|
||||
R.id.manage_people -> managePeople()
|
||||
R.id.mark_as_unread -> markAsUnread()
|
||||
R.id.pin_conversation -> pinConversation(true)
|
||||
R.id.unpin_conversation -> pinConversation(false)
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
@ -852,6 +855,24 @@ class ThreadActivity : SimpleActivity() {
|
||||
return participants
|
||||
}
|
||||
|
||||
private fun pinConversation(pin: Boolean) {
|
||||
if (pin) {
|
||||
config.addPinnedConversationByThreadId(threadId)
|
||||
} else {
|
||||
config.removePinnedConversationByThreadId(threadId)
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPinBtnVisibility(menu: Menu) {
|
||||
val pinnedConversations = config.pinnedConversations
|
||||
menu.findItem(R.id.pin_conversation).isVisible = !pinnedConversations.contains(threadId.toString())
|
||||
menu.findItem(R.id.unpin_conversation).isVisible = pinnedConversations.contains(threadId.toString())
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Subscribe(threadMode = ThreadMode.ASYNC)
|
||||
fun refreshMessages(event: Events.RefreshMessages) {
|
||||
|
@ -22,10 +22,7 @@ import com.simplemobiletools.commons.views.FastScroller
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.extensions.deleteConversation
|
||||
import com.simplemobiletools.smsmessenger.extensions.getSmsDraft
|
||||
import com.simplemobiletools.smsmessenger.extensions.markThreadMessagesRead
|
||||
import com.simplemobiletools.smsmessenger.extensions.markThreadMessagesUnread
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
import kotlinx.android.synthetic.main.item_conversation.view.*
|
||||
@ -43,11 +40,16 @@ class ConversationsAdapter(
|
||||
override fun getActionMenuId() = R.menu.cab_conversations
|
||||
|
||||
override fun prepareActionMode(menu: Menu) {
|
||||
val selectedItems = getSelectedItems()
|
||||
|
||||
menu.apply {
|
||||
findItem(R.id.cab_block_number).isVisible = isNougatPlus()
|
||||
findItem(R.id.cab_add_number_to_contact).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_dial_number).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_copy_number).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_add_number_to_contact).isVisible = isOneItemSelected() && selectedItems.firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_dial_number).isVisible = isOneItemSelected() && selectedItems.firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_copy_number).isVisible = isOneItemSelected() && selectedItems.firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_mark_as_read).isVisible = selectedItems.any { !it.read }
|
||||
findItem(R.id.cab_mark_as_unread).isVisible = selectedItems.any { it.read }
|
||||
checkPinBtnVisibility(this)
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,6 +66,8 @@ class ConversationsAdapter(
|
||||
R.id.cab_delete -> askConfirmDelete()
|
||||
R.id.cab_mark_as_read -> markAsRead()
|
||||
R.id.cab_mark_as_unread -> markAsUnread()
|
||||
R.id.cab_pin_conversation -> pinConversation(true)
|
||||
R.id.cab_unpin_conversation -> pinConversation(false)
|
||||
R.id.cab_select_all -> selectAll()
|
||||
}
|
||||
}
|
||||
@ -237,6 +241,31 @@ class ConversationsAdapter(
|
||||
|
||||
private fun getSelectedItems() = conversations.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
|
||||
private fun pinConversation(pin: Boolean) {
|
||||
val conversations = getSelectedItems()
|
||||
if (conversations.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (pin) {
|
||||
activity.config.addPinnedConversations(conversations)
|
||||
} else {
|
||||
activity.config.removePinnedConversations(conversations)
|
||||
}
|
||||
|
||||
activity.runOnUiThread {
|
||||
refreshMessages()
|
||||
finishActMode()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPinBtnVisibility(menu: Menu) {
|
||||
val pinnedConversations = activity.config.pinnedConversations
|
||||
val selectedConversations = getSelectedItems()
|
||||
menu.findItem(R.id.cab_pin_conversation).isVisible = selectedConversations.any { !pinnedConversations.contains(it.threadId.toString()) }
|
||||
menu.findItem(R.id.cab_unpin_conversation).isVisible = selectedConversations.any { pinnedConversations.contains(it.threadId.toString()) }
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
if (!activity.isDestroyed && !activity.isFinishing) {
|
||||
@ -265,6 +294,8 @@ class ConversationsAdapter(
|
||||
draft_indicator.beVisibleIf(smsDraft != null)
|
||||
draft_indicator.setTextColor(adjustedPrimaryColor)
|
||||
|
||||
pin_indicator.beVisibleIf(activity.config.pinnedConversations.contains(conversation.threadId.toString()))
|
||||
|
||||
conversation_frame.isSelected = selectedKeys.contains(conversation.hashCode())
|
||||
|
||||
conversation_address.apply {
|
||||
|
@ -0,0 +1,77 @@
|
||||
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.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
|
||||
}
|
||||
|
||||
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()
|
||||
callback(file)
|
||||
dismiss()
|
||||
}
|
||||
else -> activity.toast(R.string.invalid_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
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 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.*
|
||||
|
||||
class ImportMessagesDialog(
|
||||
private val activity: SimpleActivity,
|
||||
private val path: String,
|
||||
) {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
ensureBackgroundThread {
|
||||
MessagesImporter(activity).importMessages(path) {
|
||||
handleParseResult(it)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.simplemobiletools.smsmessenger.extensions
|
||||
|
||||
import android.content.ContentValues
|
||||
|
||||
inline fun <T> List<T>.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? {
|
||||
var index = 0
|
||||
for (item in this) {
|
||||
@ -9,3 +11,22 @@ inline fun <T> List<T>.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun Map<String, Any>.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
|
||||
}
|
||||
|
@ -255,6 +255,20 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList<
|
||||
return conversations
|
||||
}
|
||||
|
||||
fun Context.getConversationIds(): List<Long> {
|
||||
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} ASC"
|
||||
val conversationIds = mutableListOf<Long>()
|
||||
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 {
|
||||
|
@ -0,0 +1,20 @@
|
||||
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
|
||||
}
|
@ -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<Map<String, Any>>(){}.type, MapDeserializerDoubleAsIntFix())
|
||||
val gson : Gson = gsonBuilder.create()
|
||||
|
@ -0,0 +1,60 @@
|
||||
package com.simplemobiletools.smsmessenger.extensions.gson
|
||||
|
||||
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 <T> JsonElement.safeConversion(converter: () -> T?): T? {
|
||||
|
||||
return try {
|
||||
converter()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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<Map<String, Any>?> {
|
||||
@Throws(JsonParseException::class)
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Map<String, Any>? {
|
||||
return read(json) as Map<String, Any>?
|
||||
}
|
||||
|
||||
fun read(element: JsonElement): Any? {
|
||||
when {
|
||||
element.isJsonArray -> {
|
||||
val list: MutableList<Any?> = ArrayList()
|
||||
val arr = element.asJsonArray
|
||||
for (anArr in arr) {
|
||||
list.add(read(anArr))
|
||||
}
|
||||
return list
|
||||
}
|
||||
element.isJsonObject -> {
|
||||
val map: MutableMap<String, Any?> = 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
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.content.Context
|
||||
import com.simplemobiletools.commons.helpers.BaseConfig
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
import java.util.HashSet
|
||||
|
||||
class Config(context: Context) : BaseConfig(context) {
|
||||
companion object {
|
||||
@ -33,4 +35,44 @@ class Config(context: Context) : BaseConfig(context) {
|
||||
var mmsFileSizeLimit: Long
|
||||
get() = prefs.getLong(MMS_FILE_SIZE_LIMIT, FILE_SIZE_1_MB)
|
||||
set(mmsFileSizeLimit) = prefs.edit().putLong(MMS_FILE_SIZE_LIMIT, mmsFileSizeLimit).apply()
|
||||
|
||||
var pinnedConversations: Set<String>
|
||||
get() = prefs.getStringSet(PINNED_CONVERSATIONS, HashSet<String>())!!
|
||||
set(pinnedConversations) = prefs.edit().putStringSet(PINNED_CONVERSATIONS, pinnedConversations).apply()
|
||||
|
||||
fun addPinnedConversationByThreadId(threadId: Long) {
|
||||
pinnedConversations = pinnedConversations.plus(threadId.toString())
|
||||
}
|
||||
|
||||
fun addPinnedConversations(conversations: List<Conversation>) {
|
||||
pinnedConversations = pinnedConversations.plus(conversations.map { it.threadId.toString() })
|
||||
}
|
||||
|
||||
fun removePinnedConversationByThreadId(threadId: Long) {
|
||||
pinnedConversations = pinnedConversations.minus(threadId.toString())
|
||||
}
|
||||
|
||||
fun removePinnedConversations(conversations: List<Conversation>) {
|
||||
pinnedConversations = pinnedConversations.minus(conversations.map { it.threadId.toString() })
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
}
|
||||
|
@ -17,6 +17,14 @@ const val USE_SIMPLE_CHARACTERS = "use_simple_characters"
|
||||
const val LOCK_SCREEN_VISIBILITY = "lock_screen_visibility"
|
||||
const val ENABLE_DELIVERY_REPORTS = "enable_delivery_reports"
|
||||
const val MMS_FILE_SIZE_LIMIT = "mms_file_size_limit"
|
||||
const val PINNED_CONVERSATIONS = "pinned_conversations"
|
||||
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"
|
||||
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"
|
||||
|
@ -0,0 +1,68 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.provider.Telephony.*
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.*
|
||||
import com.simplemobiletools.smsmessenger.models.ExportedMessage
|
||||
import java.io.File
|
||||
|
||||
class MessagesImporter(private val context: Context) {
|
||||
enum class ImportResult {
|
||||
IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL, IMPORT_NOTHING_NEW
|
||||
}
|
||||
|
||||
private val gson = Gson()
|
||||
private val messageWriter = MessagesWriter(context)
|
||||
private val config = context.config
|
||||
private var messagesImported = 0
|
||||
private var messagesFailed = 0
|
||||
|
||||
fun importMessages(path: String, onProgress: (total: Int, current: Int) -> Unit = { _, _ -> }, callback: (result: ImportResult) -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
try {
|
||||
|
||||
val inputStream = if (path.contains("/")) {
|
||||
File(path).inputStream()
|
||||
} else {
|
||||
context.assets.open(path)
|
||||
}
|
||||
|
||||
inputStream.bufferedReader().use { reader ->
|
||||
val json = reader.readText()
|
||||
val type = object : TypeToken<List<ExportedMessage>>() {}.type
|
||||
val messages = gson.fromJson<List<ExportedMessage>>(json, type)
|
||||
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 ->
|
||||
messageWriter.writeSmsMessage(backup)
|
||||
messagesImported++
|
||||
onProgress.invoke(totalMessages, messagesImported)
|
||||
}
|
||||
}
|
||||
if (config.importMms) {
|
||||
message.mms?.forEach { backup ->
|
||||
messageWriter.writeMmsMessage(backup)
|
||||
messagesImported++
|
||||
onProgress.invoke(totalMessages, messagesImported)
|
||||
}
|
||||
}
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
messagesFailed++
|
||||
}
|
||||
|
||||
callback.invoke(
|
||||
when {
|
||||
messagesImported == 0 -> IMPORT_FAIL
|
||||
messagesFailed > 0 -> IMPORT_PARTIAL
|
||||
else -> IMPORT_OK
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.Telephony.Mms
|
||||
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
|
||||
import com.simplemobiletools.smsmessenger.models.SmsBackup
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
class MessagesReader(private val context: Context) {
|
||||
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(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: (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) {
|
||||
"${Mms.THREAD_ID} = ? AND ${Mms.TEXT_ONLY} = ?"
|
||||
} else {
|
||||
"${Mms.THREAD_ID} = ?"
|
||||
}
|
||||
|
||||
val selectionArgs = if (includeTextOnlyAttachment) {
|
||||
arrayOf(threadId.toString(), "1")
|
||||
} 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)
|
||||
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): List<MmsPart> {
|
||||
val parts = mutableListOf<MmsPart>()
|
||||
val uri = if (isQPlus()) Mms.Part.CONTENT_URI else Uri.parse("content://mms/part")
|
||||
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, 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 -> {
|
||||
usePart(partId) { stream ->
|
||||
Base64.encodeToString(stream.readBytes(), Base64.DEFAULT)
|
||||
}
|
||||
}
|
||||
}
|
||||
parts.add(MmsPart(contentDisposition, charset, contentId, contentLocation, contentType, ctStart, ctType, filename, name, sequenceOrder, text, data))
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
@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")
|
||||
try {
|
||||
val stream = context.contentResolver.openInputStream(partUri) ?: return ""
|
||||
stream.use {
|
||||
return block(stream)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun getMmsAddresses(messageId: Long): List<MmsAddress> {
|
||||
val addresses = mutableListOf<MmsAddress>()
|
||||
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())
|
||||
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
|
||||
}
|
||||
|
||||
fun getMessagesCount(): Int {
|
||||
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 {
|
||||
val cursor = context.contentResolver.query(
|
||||
uri, null, null, null, null
|
||||
) ?: return 0
|
||||
cursor.use {
|
||||
return cursor.count
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.Telephony.Mms
|
||||
import android.provider.Telephony.Sms
|
||||
import android.util.Base64
|
||||
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.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
|
||||
|
||||
class MessagesWriter(private val context: Context) {
|
||||
private val INVALID_ID = -1L
|
||||
private val contentResolver = context.contentResolver
|
||||
|
||||
fun writeSmsMessage(smsBackup: SmsBackup) {
|
||||
val contentValues = smsBackup.toContentValues()
|
||||
val threadId = Utils.getOrCreateThreadId(context, smsBackup.address)
|
||||
contentValues.put(Sms.THREAD_ID, threadId)
|
||||
if (!smsExist(smsBackup)) {
|
||||
contentResolver.insert(Sms.CONTENT_URI, contentValues)
|
||||
}
|
||||
}
|
||||
|
||||
private fun smsExist(smsBackup: SmsBackup): Boolean {
|
||||
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
|
||||
}
|
||||
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, 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
|
||||
val contentValues = mmsBackup.toContentValues()
|
||||
val threadId = getMmsThreadId(mmsBackup)
|
||||
if (threadId != INVALID_ID) {
|
||||
contentValues.put(Mms.THREAD_ID, threadId)
|
||||
if (!mmsExist(mmsBackup)) {
|
||||
contentResolver.insert(Mms.CONTENT_URI, contentValues)
|
||||
}
|
||||
val messageId = getMmsId(mmsBackup)
|
||||
if (messageId != INVALID_ID) {
|
||||
mmsBackup.parts.forEach { writeMmsPart(it, messageId) }
|
||||
mmsBackup.addresses.forEach { writeMmsAddress(it, messageId) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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(Mms._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 (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())
|
||||
var exists = false
|
||||
context.queryCursor(addressUri, projection, selection, selectionArgs) {
|
||||
exists = it.count > 0
|
||||
}
|
||||
return exists
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun writeMmsAddress(mmsAddress: MmsAddress, messageId: Long) {
|
||||
if (!mmsAddressExist(mmsAddress, messageId)) {
|
||||
val addressUri = if (isRPlus()) {
|
||||
Mms.Addr.getAddrUriForMessage(messageId.toString())
|
||||
} else {
|
||||
Uri.parse("content://mms/$messageId/addr")
|
||||
}
|
||||
|
||||
val contentValues = mmsAddress.toContentValues()
|
||||
contentValues.put(Mms.Addr.MSG_ID, messageId)
|
||||
contentResolver.insert(addressUri, contentValues)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun writeMmsPart(mmsPart: MmsPart, messageId: Long) {
|
||||
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)
|
||||
try {
|
||||
if (partUri != null) {
|
||||
if (mmsPart.isNonText()) {
|
||||
contentResolver.openOutputStream(partUri).use {
|
||||
val arr = Base64.decode(mmsPart.data, Base64.DEFAULT)
|
||||
it!!.write(arr)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
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 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
|
||||
}
|
||||
return exists
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ExportedMessage(
|
||||
@SerializedName("sms")
|
||||
val sms: List<SmsBackup>?,
|
||||
@SerializedName("mms")
|
||||
val mms: List<MmsBackup>?,
|
||||
)
|
@ -0,0 +1,26 @@
|
||||
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("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,
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
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_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("tr_id")
|
||||
val transactionId: String?,
|
||||
@SerializedName("addresses")
|
||||
val addresses: List<MmsAddress>,
|
||||
@SerializedName("parts")
|
||||
val parts: List<MmsPart>,
|
||||
) {
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
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("fn")
|
||||
val filename: String?,
|
||||
@SerializedName("name")
|
||||
val name: String?,
|
||||
@SerializedName("seq")
|
||||
val sequenceOrder: Int,
|
||||
@SerializedName("text")
|
||||
val text: String?,
|
||||
@SerializedName("data")
|
||||
val data: 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")
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
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("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,
|
||||
@SerializedName("service_center")
|
||||
val serviceCenter: String?
|
||||
) {
|
||||
|
||||
fun toContentValues(): ContentValues {
|
||||
return contentValuesOf(
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
70
app/src/main/res/layout/dialog_export_messages.xml
Normal file
70
app/src/main/res/layout/dialog_export_messages.xml
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/export_messages_scrollview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/export_messages_holder"
|
||||
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">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/export_messages_folder_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/folder"
|
||||
android:textSize="@dimen/smaller_text_size" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/export_messages_folder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/activity_margin"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/small_margin"
|
||||
android:paddingEnd="@dimen/small_margin"
|
||||
android:paddingBottom="@dimen/small_margin" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
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" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyEditText
|
||||
android:id="@+id/export_messages_filename"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/small_margin"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/normal_margin"
|
||||
android:paddingEnd="@dimen/small_margin"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
|
||||
android:id="@+id/export_sms_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/small_margin"
|
||||
android:paddingBottom="@dimen/small_margin"
|
||||
android:text="@string/export_sms" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
|
||||
android:id="@+id/export_mms_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/small_margin"
|
||||
android:paddingBottom="@dimen/small_margin"
|
||||
android:text="@string/export_mms" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
35
app/src/main/res/layout/dialog_import_messages.xml
Normal file
35
app/src/main/res/layout/dialog_import_messages.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/import_messages_scrollview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/import_messages_holder"
|
||||
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">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
|
||||
android:id="@+id/import_sms_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/small_margin"
|
||||
android:paddingBottom="@dimen/small_margin"
|
||||
android:text="@string/import_sms" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
|
||||
android:id="@+id/import_mms_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/small_margin"
|
||||
android:paddingBottom="@dimen/small_margin"
|
||||
android:text="@string/import_mms" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -26,6 +26,24 @@
|
||||
android:layout_marginStart="@dimen/normal_margin"
|
||||
android:layout_marginEnd="@dimen/normal_margin" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/pin_indicator_l"
|
||||
android:layout_width="@dimen/normal_icon_size"
|
||||
android:layout_height="@dimen/normal_icon_size"
|
||||
android:layout_alignTop="@+id/conversation_address"
|
||||
android:layout_alignBottom="@+id/conversation_date"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="@dimen/normal_margin"
|
||||
android:layout_marginEnd="@dimen/normal_margin">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pin_indicator"
|
||||
android:layout_width="@dimen/pin_icon_size"
|
||||
android:layout_height="@dimen/pin_icon_size"
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@drawable/ic_pin" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/conversation_address"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -33,6 +33,16 @@
|
||||
android:id="@+id/cab_mark_as_unread"
|
||||
android:title="@string/mark_as_unread"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/cab_pin_conversation"
|
||||
android:icon="@drawable/ic_pin"
|
||||
android:title="@string/pin_conversation"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/cab_unpin_conversation"
|
||||
android:icon="@drawable/ic_unpin"
|
||||
android:title="@string/unpin_conversation"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/cab_select_all"
|
||||
android:title="@string/select_all"
|
||||
|
@ -6,6 +6,14 @@
|
||||
android:icon="@drawable/ic_search_vector"
|
||||
android:title="@string/search"
|
||||
app:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/import_messages"
|
||||
android:title="@string/import_messages"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/export_messages"
|
||||
android:title="@string/export_messages"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/settings"
|
||||
android:title="@string/settings"
|
||||
|
@ -19,4 +19,12 @@
|
||||
android:id="@+id/mark_as_unread"
|
||||
android:title="@string/mark_as_unread"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/pin_conversation"
|
||||
android:title="@string/pin_conversation"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/unpin_conversation"
|
||||
android:title="@string/unpin_conversation"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Odesílatel nepodporuje odpovědi</string>
|
||||
<string name="draft">Návrh</string>
|
||||
<string name="sending">Odesílá se…</string>
|
||||
<string name="export_messages">Export zpráv</string>
|
||||
<string name="import_messages">Import zpráv</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nová konverzace</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="export_messages">Export zpráv</string>
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import zpráv</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Proč aplikace vyžaduje přístup k internetu?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Afsender understøtter ikke svar</string>
|
||||
<string name="draft">Udkast</string>
|
||||
<string name="sending">Sender…</string>
|
||||
<string name="export_messages">Eksporter beskeder</string>
|
||||
<string name="import_messages">Importer beskeder</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Ny Samtale</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Eksporter beskeder</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Importer beskeder</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Hvorfor kræver appen adgang til internettet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Der Absender unterstützt keine Antworten.</string>
|
||||
<string name="draft">Entwurf</string>
|
||||
<string name="sending">Sende…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Neuer Chat</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Warum benötigt diese App Internetzugriff?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Ο αποστολέας δεν υποστηρίζει απαντήσεις</string>
|
||||
<string name="draft">Πρόχειρο</string>
|
||||
<string name="sending">Γίνεται αποστολή…</string>
|
||||
<string name="export_messages">Εξαγωγή μηνυμάτων</string>
|
||||
<string name="import_messages">Εισαγωγή μηνυμάτων</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Νέα συνομιλία</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Αλλαγή μεγέθους απεσταλμένων εικόνων MMS</string>
|
||||
<string name="mms_file_size_limit_none">Χωρίς όριο</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Εξαγωγή μηνυμάτων</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Εισαγωγή μηνυμάτων</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Γιατί η εφαρμογή απαιτεί πρόσβαση στο Internet;</string>
|
||||
<string name="faq_1_text">Δυστυχώς, απαιτείται για την αποστολή συνημμένων MMS. Το να μην είμαστε σε θέση να στείλουμε MMS θα αποτελούσε πραγματικά τεράστιο μειονέκτημα σε σύγκριση με άλλες εφαρμογές, επομένως αποφασίσαμε να ακολουθήσουμε αυτόν τον δρόμο.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nueva conversación</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">¿Por qué la aplicación requiere acceso a internet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Lähettäjä ei tue vastauksia</string>
|
||||
<string name="draft">Luonnos</string>
|
||||
<string name="sending">Lähetetään…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Uusi keskustelu</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Miksi sovellus vaatii Internet-yhteyden?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">L\'expéditeur n\'accepte pas les réponses</string>
|
||||
<string name="draft">Brouillon</string>
|
||||
<string name="sending">Envoi en cours…</string>
|
||||
<string name="export_messages">Export de messages</string>
|
||||
<string name="import_messages">Import de messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nouvelle conversation</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Redimensionner les images MMS envoyées</string>
|
||||
<string name="mms_file_size_limit_none">Pas de limite</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export de messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import de messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Pourquoi cette application a besoin d\'un accès à internet ?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nova conversa</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Por que o aplicativo necesita acceder a Internet?</string>
|
||||
<string name="faq_1_text">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.</string>
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Percakapan baru</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Mengapa aplikasi membutuhkan akses ke internet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Il mittente non accetta risposte</string>
|
||||
<string name="draft">Bozza</string>
|
||||
<string name="sending">Invio…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nuova conversazione</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Perché l\'applicazione richiede l\'accesso ad internet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">新しい会話</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">なぜアプリ使用にインターネットへのアクセスが必要なのですか?</string>
|
||||
<string name="faq_1_text">生憎、MMS(マルチメディアメッセージサービス)にインターネットが必要となります。他のアプリと比較して、MMSを使用出来ないと大きな損になるので、こうすることに決めました。
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Naujas pokalbis</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Why does the app require access to the internet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">പുതിയ സംഭാഷണം</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">അപ്ലിക്കേഷന് ഇന്റർനെറ്റിലേക്ക് ആവശ്യമായി വരുന്നത് എന്തുകൊണ്ട്?</string>
|
||||
<string name="faq_1_text">നിർഭാഗ്യവശാൽ, MMS അറ്റാച്ചുമെന്റുകൾ അയക്കുന്നതിനു ഇത് ആവശ്യമാണ്. മറ്റ് ആപ്ലിക്കേഷനുകളുമായി താരതമ്യപ്പെടുത്തുമ്പോൾ MMS അയയ്ക്കാൻ കഴിയുന്നില്ല എന്നത് ഒരു വലിയ പോരായ്മയാണ്, അതിനാൽ ഞങ്ങൾ ഈ റൂട്ടിൽ പോകാൻ തീരുമാനിച്ചു.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Afzender ondersteunt geen antwoorden</string>
|
||||
<string name="draft">Concept</string>
|
||||
<string name="sending">Versturen…</string>
|
||||
<string name="export_messages">Berichten exporteren</string>
|
||||
<string name="import_messages">Berichten importeren</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nieuw gesprek</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Afbeelding verkleinen voor MMS</string>
|
||||
<string name="mms_file_size_limit_none">Geen limiet</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Berichten exporteren</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Berichten importeren</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Waarom heeft deze app toegang nodig tot het internet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Nadawca nie obsługuje odpowiedzi</string>
|
||||
<string name="draft">Szkic</string>
|
||||
<string name="sending">Wysyłanie…</string>
|
||||
<string name="export_messages">Eksportuj wiadomości</string>
|
||||
<string name="import_messages">Importuj wiadomości</string>
|
||||
<string name="pin_conversation">Przypnij na górze</string>
|
||||
<string name="unpin_conversation">Odepnij</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nowa rozmowa</string>
|
||||
@ -55,6 +55,17 @@
|
||||
<string name="mms_file_size_limit">Rozmiar wysyłanych obrazków w MMS-ach</string>
|
||||
<string name="mms_file_size_limit_none">Bez limitu</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Eksportuj wiadomości</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Importuj wiadomości</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Dlaczego aplikacja wymaga dostępu do Internetu?</string>
|
||||
<string name="faq_1_text">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ą.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">O remetente não aceita respostas</string>
|
||||
<string name="draft">Rascunho</string>
|
||||
<string name="sending">A enviar…</string>
|
||||
<string name="export_messages">Exportar mensagens</string>
|
||||
<string name="import_messages">Importar mensagens</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nova conversa</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Exportar mensagens</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Importar mensagens</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Porque é que a aplicação necessita de aceder à Internet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Отправитель не поддерживает ответы</string>
|
||||
<string name="draft">Черновик</string>
|
||||
<string name="sending">Отправка…</string>
|
||||
<string name="export_messages">Экспорт сообщений</string>
|
||||
<string name="import_messages">Импорт сообщений</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Новая переписка</string>
|
||||
@ -55,6 +55,17 @@
|
||||
<string name="mms_file_size_limit">Изменять размер отправляемых в MMS изображений</string>
|
||||
<string name="mms_file_size_limit_none">Нет ограничения</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Экспорт сообщений</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Импорт сообщений</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Почему приложение требует доступ к интернету?</string>
|
||||
<string name="faq_1_text">К сожалению, это необходимо для отправки вложений MMS. Отсутствие возможности отправлять MMS-сообщения было бы огромным недостатком нашего приложения по сравнению с другими, поэтому мы решили пойти этим путём.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Odosielateľ nepodporuje odpovede</string>
|
||||
<string name="draft">Koncept</string>
|
||||
<string name="sending">Odosiela sa…</string>
|
||||
<string name="export_messages">Exportovať správy</string>
|
||||
<string name="import_messages">Importovať správy</string>
|
||||
<string name="pin_conversation">Pripnúť na vrch</string>
|
||||
<string name="unpin_conversation">Odopnúť</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nová konverzácia</string>
|
||||
@ -55,6 +55,17 @@
|
||||
<string name="mms_file_size_limit">Zmenšiť MMS obrázky pri odosielaní</string>
|
||||
<string name="mms_file_size_limit_none">Žiadny limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Správy</string>
|
||||
<string name="export_messages">Exportovať správy</string>
|
||||
<string name="export_sms">Exportovať SMS</string>
|
||||
<string name="export_mms">Exportovať MMS</string>
|
||||
<string name="export_unchecked_error_message">Označte buď exportovanie SMS alebo exportovanie MMS</string>
|
||||
<string name="import_messages">Importovať správy</string>
|
||||
<string name="import_sms">Importovať SMS</string>
|
||||
<string name="import_mms">Importovať MMS</string>
|
||||
<string name="import_unchecked_error_message">Označte buď importovanie SMS alebo importovanie MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Prečo vyžaduje apka prístup na internet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Yeni görüşme</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Uygulama neden internete erişim gerektiriyor?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Нове листування</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Чому додаток потрубує доступу до інтернету?</string>
|
||||
<string name="faq_1_text">Нажаль, це необхідно для відправки вкладень MMS. Неспроможність надсилати MMS-повідомлення була б великим недоліком нашого додатку порівняно з іншими, тому ми так зробили.
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">新的对话</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">为什么该应用需要访问互联网?</string>
|
||||
<string name="faq_1_text">很遗憾这对于发送彩信附件是必须的。如果不能发送彩信的话这相比其他应用会是一个巨大的劣势,所以我们决定这么采取现在的方式。
|
||||
|
@ -5,4 +5,5 @@
|
||||
<dimen name="play_outline_size">36dp</dimen>
|
||||
<dimen name="attachment_preview_size">60dp</dimen>
|
||||
<dimen name="remove_attachment_size">24dp</dimen>
|
||||
<dimen name="pin_icon_size">15dp</dimen>
|
||||
</resources>
|
||||
|
@ -15,8 +15,8 @@
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">New conversation</string>
|
||||
@ -53,6 +53,17 @@
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="export_unchecked_error_message">Check at least one of Export SMS or Export MMS</string>
|
||||
<string name="import_unchecked_error_message">Check at least one of Import SMS or Import MMS</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Why does the app require access to the internet?</string>
|
||||
<string name="faq_1_text">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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user