diff --git a/CHANGELOG.md b/CHANGELOG.md
index 569884ab..3036521f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,29 @@
Changelog
==========
+Version 5.2.0 *(2020-05-30)*
+----------------------------
+
+ * Cache the conversations shown on the main screen to drastically improve their loading speed
+ * Allow adding newlines with the Enter button
+ * Properly handle a new View third party intent
+ * Added some other translation, UI and stability improvements
+
+Version 5.1.4 *(2020-05-23)*
+----------------------------
+
+ * Improved the performance at checking if a number is blocked
+ * Use capital letters at sentences at writing a message
+ * Allow blocking numbers from the main screen
+ * Fixed a glitch with adding new people to a conversation
+
+Version 5.1.3 *(2020-05-20)*
+----------------------------
+
+ * Adding support for accessing private contacts stored in Simple Contacts Pro (to be added soon)
+ * Make sure the conversations are sorted properly
+ * Added some other UI and translation improvements
+
Version 5.1.2 *(2020-05-13)*
----------------------------
diff --git a/README.md b/README.md
index 574cb344..6ab5c2c8 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ Reddit:
https://www.reddit.com/r/SimpleMobileTools
+
diff --git a/app/build.gradle b/app/build.gradle
index 46335361..7be53d19 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
@@ -16,8 +17,8 @@ android {
applicationId "com.simplemobiletools.smsmessenger"
minSdkVersion 22
targetSdkVersion 29
- versionCode 5
- versionName "5.1.2"
+ versionCode 9
+ versionName "5.2.0"
setProperty("archivesBaseName", "sms-messenger")
}
@@ -56,8 +57,12 @@ android {
}
dependencies {
- implementation 'com.simplemobiletools:commons:5.27.29'
+ implementation 'com.simplemobiletools:commons:5.28.25'
implementation 'org.greenrobot:eventbus:3.2.0'
implementation 'com.klinkerapps:android-smsmms:5.2.6'
implementation 'com.github.tibbi:IndicatorFastScroll:08f512858a'
+
+ kapt "androidx.room:room-compiler:2.2.5"
+ implementation "androidx.room:room-runtime:2.2.5"
+ annotationProcessor "androidx.room:room-compiler:2.2.5"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 770b80db..4c0108ca 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -40,12 +40,12 @@
+
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt
index 4e9ae074..68842dfc 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt
@@ -19,6 +19,7 @@ import com.simplemobiletools.smsmessenger.BuildConfig
import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.adapters.ConversationsAdapter
import com.simplemobiletools.smsmessenger.extensions.config
+import com.simplemobiletools.smsmessenger.extensions.conversationsDB
import com.simplemobiletools.smsmessenger.extensions.getConversations
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE
@@ -128,9 +129,9 @@ class MainActivity : SimpleActivity() {
handlePermission(PERMISSION_SEND_SMS) {
if (it) {
handlePermission(PERMISSION_READ_CONTACTS) {
+ initMessenger()
bus = EventBus.getDefault()
bus!!.register(this)
- initMessenger()
}
} else {
finish()
@@ -144,26 +145,7 @@ class MainActivity : SimpleActivity() {
private fun initMessenger() {
storeStateVariables()
- ensureBackgroundThread {
- val conversations = getConversations()
- runOnUiThread {
- val hasConversations = conversations.isNotEmpty()
- conversations_list.beVisibleIf(hasConversations)
- no_conversations_placeholder.beVisibleIf(!hasConversations)
- no_conversations_placeholder_2.beVisibleIf(!hasConversations)
-
- ConversationsAdapter(this, conversations, conversations_list, conversations_fastscroller) {
- Intent(this, ThreadActivity::class.java).apply {
- putExtra(THREAD_ID, (it as Conversation).id)
- putExtra(THREAD_TITLE, it.title)
- startActivity(this)
- }
- }.apply {
- conversations_list.adapter = this
- }
- }
- }
-
+ getCachedConversations()
no_conversations_placeholder_2.setOnClickListener {
launchNewConversation()
@@ -174,6 +156,80 @@ class MainActivity : SimpleActivity() {
}
}
+ private fun getCachedConversations() {
+ ensureBackgroundThread {
+ val conversations = conversationsDB.getAll().sortedByDescending { it.date }.toMutableList() as ArrayList
+ runOnUiThread {
+ setupConversations(conversations)
+ getNewConversations(conversations)
+ }
+ }
+ }
+
+ private fun getNewConversations(cachedConversations: ArrayList) {
+ val privateCursor = getMyContactsContentProviderCursorLoader().loadInBackground()
+ ensureBackgroundThread {
+ val conversations = getConversations()
+
+ // check if no message came from a privately stored contact in Simple Contacts
+ val privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
+ if (privateContacts.isNotEmpty()) {
+ conversations.filter { it.title == it.phoneNumber }.forEach { conversation ->
+ privateContacts.firstOrNull { it.phoneNumber == conversation.phoneNumber }?.apply {
+ conversation.title = name
+ conversation.photoUri = photoUri
+ }
+ }
+ }
+
+ runOnUiThread {
+ setupConversations(conversations)
+ }
+
+ conversations.forEach { clonedConversation ->
+ if (!cachedConversations.map { it.thread_id }.contains(clonedConversation.thread_id)) {
+ conversationsDB.insertOrUpdate(clonedConversation)
+ cachedConversations.add(clonedConversation)
+ }
+ }
+
+ cachedConversations.forEach { cachedConversation ->
+ if (!conversations.map { it.thread_id }.contains(cachedConversation.thread_id)) {
+ conversationsDB.delete(cachedConversation.id!!)
+ }
+ }
+
+ cachedConversations.forEach { cachedConversation ->
+ val conv = conversations.firstOrNull { it.thread_id == cachedConversation.thread_id && it.getStringToCompare() != cachedConversation.getStringToCompare() }
+ if (conv != null) {
+ conversationsDB.insertOrUpdate(conv)
+ }
+ }
+ }
+ }
+
+ private fun setupConversations(conversations: ArrayList) {
+ val hasConversations = conversations.isNotEmpty()
+ conversations_list.beVisibleIf(hasConversations)
+ no_conversations_placeholder.beVisibleIf(!hasConversations)
+ no_conversations_placeholder_2.beVisibleIf(!hasConversations)
+
+ val currAdapter = conversations_list.adapter
+ if (currAdapter == null) {
+ ConversationsAdapter(this, conversations, conversations_list, conversations_fastscroller) {
+ Intent(this, ThreadActivity::class.java).apply {
+ putExtra(THREAD_ID, (it as Conversation).thread_id)
+ putExtra(THREAD_TITLE, it.title)
+ startActivity(this)
+ }
+ }.apply {
+ conversations_list.adapter = this
+ }
+ } else {
+ (currAdapter as ConversationsAdapter).updateConversations(conversations)
+ }
+ }
+
private fun launchNewConversation() {
Intent(this, NewConversationActivity::class.java).apply {
startActivity(this)
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/NewConversationActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/NewConversationActivity.kt
index fc07761c..4baea150 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/NewConversationActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/NewConversationActivity.kt
@@ -7,6 +7,7 @@ import android.view.Menu
import android.view.WindowManager
import com.reddit.indicatorfastscroll.FastScrollItemIndicator
import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.MyContactsContentProvider
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
@@ -17,7 +18,7 @@ import com.simplemobiletools.smsmessenger.extensions.config
import com.simplemobiletools.smsmessenger.extensions.getSuggestedContacts
import com.simplemobiletools.smsmessenger.extensions.getThreadId
import com.simplemobiletools.smsmessenger.helpers.*
-import kotlinx.android.synthetic.main.activity_conversation.*
+import kotlinx.android.synthetic.main.activity_new_conversation.*
import kotlinx.android.synthetic.main.item_suggested_contact.view.*
import java.net.URLDecoder
import java.util.*
@@ -25,10 +26,11 @@ import kotlin.collections.ArrayList
class NewConversationActivity : SimpleActivity() {
private var allContacts = ArrayList()
+ private var privateContacts = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_conversation)
+ setContentView(R.layout.activity_new_conversation)
title = getString(R.string.new_conversation)
updateTextColors(new_conversation_holder)
@@ -93,9 +95,10 @@ class NewConversationActivity : SimpleActivity() {
}
private fun isThirdPartyIntent(): Boolean {
- if (intent.action == Intent.ACTION_SENDTO && intent.dataString != null) {
+ if ((intent.action == Intent.ACTION_SENDTO || intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_VIEW) && intent.dataString != null) {
val number = intent.dataString!!.removePrefix("sms:").removePrefix("smsto:").removePrefix("mms").removePrefix("mmsto:").trim()
launchThreadActivity(URLDecoder.decode(number), "")
+ finish()
return true
}
return false
@@ -103,9 +106,14 @@ class NewConversationActivity : SimpleActivity() {
private fun fetchContacts() {
fillSuggestedContacts {
- SimpleContactsHelper(this).getAvailableContacts {
+ SimpleContactsHelper(this).getAvailableContacts(false) {
allContacts = it
+ if (privateContacts.isNotEmpty()) {
+ allContacts.addAll(privateContacts)
+ allContacts.sort()
+ }
+
runOnUiThread {
setupAdapter(allContacts)
}
@@ -135,8 +143,10 @@ class NewConversationActivity : SimpleActivity() {
}
private fun fillSuggestedContacts(callback: () -> Unit) {
+ val privateCursor = getMyContactsContentProviderCursorLoader().loadInBackground()
ensureBackgroundThread {
- val suggestions = getSuggestedContacts()
+ privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
+ val suggestions = getSuggestedContacts(privateContacts)
runOnUiThread {
suggestions_holder.removeAllViews()
if (suggestions.isEmpty()) {
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt
index cd545422..83773bd2 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt
@@ -30,10 +30,7 @@ import com.klinker.android.send_message.Settings
import com.klinker.android.send_message.Transaction
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.extensions.*
-import com.simplemobiletools.commons.helpers.PERMISSION_READ_PHONE_STATE
-import com.simplemobiletools.commons.helpers.SimpleContactsHelper
-import com.simplemobiletools.commons.helpers.ensureBackgroundThread
-import com.simplemobiletools.commons.helpers.isNougatPlus
+import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.SimpleContact
import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.adapters.AutoCompleteTextViewAdapter
@@ -57,6 +54,7 @@ class ThreadActivity : SimpleActivity() {
private var threadItems = ArrayList()
private var bus: EventBus? = null
private var participants = ArrayList()
+ private var privateContacts = ArrayList()
private var messages = ArrayList()
private val availableSIMCards = ArrayList()
private var attachmentUris = LinkedHashSet()
@@ -89,6 +87,7 @@ class ThreadActivity : SimpleActivity() {
}
private fun setupThread() {
+ val privateCursor = getMyContactsContentProviderCursorLoader().loadInBackground()
ensureBackgroundThread {
messages = getMessages(threadId)
participants = if (messages.isEmpty()) {
@@ -97,6 +96,25 @@ class ThreadActivity : SimpleActivity() {
messages.first().participants
}
+ // check if no participant came from a privately stored contact in Simple Contacts
+ privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
+ if (privateContacts.isNotEmpty()) {
+ val senderNumbersToReplace = HashMap()
+ participants.filter { it.name == it.phoneNumber }.forEach { participant ->
+ privateContacts.firstOrNull { it.phoneNumber == participant.phoneNumber }?.apply {
+ senderNumbersToReplace[participant.phoneNumber] = name
+ participant.name = name
+ participant.photoUri = photoUri
+ }
+ }
+
+ messages.forEach { message ->
+ if (senderNumbersToReplace.keys.contains(message.senderName)) {
+ message.senderName = senderNumbersToReplace[message.senderName]!!
+ }
+ }
+ }
+
if (participants.isEmpty()) {
val name = intent.getStringExtra(THREAD_TITLE) ?: ""
val number = intent.getStringExtra(THREAD_NUMBER)
@@ -202,9 +220,10 @@ class ThreadActivity : SimpleActivity() {
thread_messages_list.adapter = adapter
}
- SimpleContactsHelper(this).getAvailableContacts {
+ SimpleContactsHelper(this).getAvailableContacts(false) { contacts ->
+ contacts.addAll(privateContacts)
runOnUiThread {
- val adapter = AutoCompleteTextViewAdapter(this, it)
+ val adapter = AutoCompleteTextViewAdapter(this, contacts)
add_contact_or_number.setAdapter(adapter)
add_contact_or_number.imeOptions = EditorInfo.IME_ACTION_NEXT
add_contact_or_number.setOnItemClickListener { _, _, position, _ ->
@@ -219,7 +238,7 @@ class ThreadActivity : SimpleActivity() {
}
}
- confirm_inserted_number.setOnClickListener {
+ confirm_inserted_number?.setOnClickListener {
val number = add_contact_or_number.value
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", number)
addSelectedContact(contact)
@@ -307,10 +326,9 @@ class ThreadActivity : SimpleActivity() {
}
private fun blockNumber() {
- val baseString = R.string.block_confirmation
- val numbers = participants.map { it.phoneNumber }.toTypedArray()
+ val numbers = participants.map { it.phoneNumber }
val numbersString = TextUtils.join(", ", numbers)
- val question = String.format(resources.getString(baseString), numbersString)
+ val question = String.format(resources.getString(R.string.block_confirmation), numbersString)
ConfirmationDialog(this, question) {
ensureBackgroundThread {
@@ -325,9 +343,13 @@ class ThreadActivity : SimpleActivity() {
private fun askConfirmDelete() {
ConfirmationDialog(this, getString(R.string.delete_whole_conversation_confirmation)) {
- deleteConversation(threadId)
- refreshMessages()
- finish()
+ ensureBackgroundThread {
+ deleteConversation(threadId)
+ runOnUiThread {
+ refreshMessages()
+ finish()
+ }
+ }
}
}
@@ -399,6 +421,7 @@ class ThreadActivity : SimpleActivity() {
if (!it.read) {
hadUnreadItems = true
markMessageRead(it.id, it.isMMS)
+ conversationsDB.markRead(threadId.toLong())
}
}
@@ -450,7 +473,6 @@ class ThreadActivity : SimpleActivity() {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean {
attachmentView.thread_attachment_preview.beGone()
attachmentView.thread_remove_attachment.beGone()
- showErrorToast(e?.localizedMessage ?: "")
return false
}
@@ -497,9 +519,13 @@ class ThreadActivity : SimpleActivity() {
if (attachmentUris.isNotEmpty()) {
for (uri in attachmentUris) {
- val byteArray = contentResolver.openInputStream(uri)?.readBytes() ?: continue
- val mimeType = contentResolver.getType(uri) ?: continue
- message.addMedia(byteArray, mimeType)
+ try {
+ val byteArray = contentResolver.openInputStream(uri)?.readBytes() ?: continue
+ val mimeType = contentResolver.getType(uri) ?: continue
+ message.addMedia(byteArray, mimeType)
+ } catch (e: Exception) {
+ showErrorToast(e)
+ }
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/AutoCompleteTextViewAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/AutoCompleteTextViewAdapter.kt
index 75611076..68c38b7f 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/AutoCompleteTextViewAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/AutoCompleteTextViewAdapter.kt
@@ -13,7 +13,6 @@ import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: ArrayList) : ArrayAdapter(activity, 0, contacts) {
-
var resultList = ArrayList()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
@@ -25,6 +24,12 @@ class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: Ar
listItem!!.apply {
tag = contact.name.isNotEmpty()
+ // clickable and focusable properties seem to break Autocomplete clicking, so remove them
+ findViewById(R.id.item_contact_frame).apply {
+ isClickable = false
+ isFocusable = false
+ }
+
findViewById(R.id.item_contact_name).text = contact.name
findViewById(R.id.item_contact_number).text = contact.phoneNumber
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt
index ed427e89..4d294124 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt
@@ -2,6 +2,7 @@ package com.simplemobiletools.smsmessenger.adapters
import android.content.Intent
import android.graphics.Typeface
+import android.text.TextUtils
import android.view.Menu
import android.view.View
import android.view.ViewGroup
@@ -9,11 +10,13 @@ import android.widget.TextView
import com.bumptech.glide.Glide
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
+import com.simplemobiletools.commons.extensions.addBlockedNumber
import com.simplemobiletools.commons.extensions.formatDateOrTime
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.KEY_PHONE
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
+import com.simplemobiletools.commons.helpers.isNougatPlus
import com.simplemobiletools.commons.views.FastScroller
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.smsmessenger.R
@@ -35,6 +38,7 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
override fun prepareActionMode(menu: Menu) {
menu.apply {
findItem(R.id.cab_add_number_to_contact).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
+ findItem(R.id.cab_block_number).isVisible = isNougatPlus()
}
}
@@ -45,6 +49,7 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
when (id) {
R.id.cab_add_number_to_contact -> addNumberToContact()
+ R.id.cab_block_number -> askConfirmBlock()
R.id.cab_select_all -> selectAll()
R.id.cab_delete -> askConfirmDelete()
}
@@ -54,9 +59,9 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
override fun getIsItemSelectable(position: Int) = true
- override fun getItemSelectionKey(position: Int) = conversations.getOrNull(position)?.id
+ override fun getItemSelectionKey(position: Int) = conversations.getOrNull(position)?.thread_id
- override fun getItemKeyPosition(key: Int) = conversations.indexOfFirst { it.id == key }
+ override fun getItemKeyPosition(key: Int) = conversations.indexOfFirst { it.thread_id == key }
override fun onActionModeCreated() {}
@@ -74,6 +79,37 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
override fun getItemCount() = conversations.size
+ private fun askConfirmBlock() {
+ val numbers = getSelectedItems().distinctBy { it.phoneNumber }.map { it.phoneNumber }
+ val numbersString = TextUtils.join(", ", numbers)
+ val question = String.format(resources.getString(R.string.block_confirmation), numbersString)
+
+ ConfirmationDialog(activity, question) {
+ blockNumbers()
+ }
+ }
+
+ private fun blockNumbers() {
+ if (selectedKeys.isEmpty()) {
+ return
+ }
+
+ val numbersToBlock = getSelectedItems()
+ val positions = getSelectedItemPositions()
+ conversations.removeAll(numbersToBlock)
+
+ ensureBackgroundThread {
+ numbersToBlock.map { it.phoneNumber }.forEach { number ->
+ activity.addBlockedNumber(number)
+ }
+
+ activity.runOnUiThread {
+ removeSelectedItems(positions)
+ finishActMode()
+ }
+ }
+ }
+
private fun askConfirmDelete() {
val itemsCnt = selectedKeys.size
val items = resources.getQuantityString(R.plurals.delete_conversations, itemsCnt, itemsCnt)
@@ -93,10 +129,10 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
return
}
- val conversationsToRemove = conversations.filter { selectedKeys.contains(it.id) } as ArrayList
+ val conversationsToRemove = conversations.filter { selectedKeys.contains(it.thread_id) } as ArrayList
val positions = getSelectedItemPositions()
conversationsToRemove.forEach {
- activity.deleteConversation(it.id)
+ activity.deleteConversation(it.thread_id)
}
conversations.removeAll(conversationsToRemove)
@@ -128,7 +164,7 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
}
}
- private fun getSelectedItems() = conversations.filter { selectedKeys.contains(it.id) } as ArrayList
+ private fun getSelectedItems() = conversations.filter { selectedKeys.contains(it.thread_id) } as ArrayList
override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder)
@@ -137,9 +173,18 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
}
}
+ fun updateConversations(newConversations: ArrayList) {
+ val oldHashCode = conversations.hashCode()
+ val newHashCode = newConversations.hashCode()
+ if (newHashCode != oldHashCode) {
+ conversations = newConversations
+ notifyDataSetChanged()
+ }
+ }
+
private fun setupView(view: View, conversation: Conversation) {
view.apply {
- conversation_frame.isSelected = selectedKeys.contains(conversation.id)
+ conversation_frame.isSelected = selectedKeys.contains(conversation.thread_id)
conversation_address.text = conversation.title
conversation_body_short.text = conversation.snippet
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt
index 0f5a6531..337452f9 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt
@@ -44,6 +44,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private val roundedCornersRadius = resources.getDimension(R.dimen.normal_margin).toInt()
+
@SuppressLint("MissingPermission")
private val hasMultipleSIMCards = SubscriptionManager.from(activity).activeSubscriptionInfoList?.size ?: 0 > 1
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/databases/MessagesDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/databases/MessagesDatabase.kt
new file mode 100644
index 00000000..a514dd12
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/databases/MessagesDatabase.kt
@@ -0,0 +1,30 @@
+package com.simplemobiletools.smsmessenger.databases
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
+import com.simplemobiletools.smsmessenger.models.Conversation
+
+@Database(entities = [(Conversation::class)], version = 1)
+abstract class MessagesDatabase : RoomDatabase() {
+
+ abstract fun ConversationsDao(): ConversationsDao
+
+ companion object {
+ private var db: MessagesDatabase? = null
+
+ fun getInstance(context: Context): MessagesDatabase {
+ if (db == null) {
+ synchronized(MessagesDatabase::class) {
+ if (db == null) {
+ db = Room.databaseBuilder(context.applicationContext, MessagesDatabase::class.java, "conversations.db")
+ .build()
+ }
+ }
+ }
+ return db!!
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/ArrayList.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/ArrayList.kt
index dfaf3202..0ff90913 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/ArrayList.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/ArrayList.kt
@@ -2,5 +2,8 @@ package com.simplemobiletools.smsmessenger.extensions
import android.text.TextUtils
import com.simplemobiletools.commons.models.SimpleContact
+import com.simplemobiletools.smsmessenger.models.Conversation
fun ArrayList.getThreadTitle() = TextUtils.join(", ", map { it.name }.toTypedArray())
+
+fun ArrayList.getHashToCompare() = map { it.getStringToCompare() }.hashCode()
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt
index dee4134e..8e89f984 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt
@@ -22,7 +22,9 @@ import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.SimpleContact
import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
+import com.simplemobiletools.smsmessenger.databases.MessagesDatabase
import com.simplemobiletools.smsmessenger.helpers.*
+import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
import com.simplemobiletools.smsmessenger.models.*
import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver
import java.util.*
@@ -30,6 +32,10 @@ import kotlin.collections.ArrayList
val Context.config: Config get() = Config.newInstance(applicationContext)
+fun Context.getMessagessDB() = MessagesDatabase.getInstance(this)
+
+val Context.conversationsDB: ConversationsDao get() = getMessagessDB().ConversationsDao()
+
fun Context.getMessages(threadId: Int): ArrayList {
val uri = Sms.CONTENT_URI
val projection = arrayOf(
@@ -48,6 +54,7 @@ fun Context.getMessages(threadId: Int): ArrayList {
val sortOrder = "${Sms._ID} DESC LIMIT 100"
val blockStatus = HashMap()
+ val blockedNumbers = getBlockedNumbers()
var messages = ArrayList()
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor
@@ -55,7 +62,7 @@ fun Context.getMessages(threadId: Int): ArrayList {
val isNumberBlocked = if (blockStatus.containsKey(senderNumber)) {
blockStatus[senderNumber]!!
} else {
- val isBlocked = isNumberBlocked(senderNumber)
+ val isBlocked = isNumberBlocked(senderNumber, blockedNumbers)
blockStatus[senderNumber] = isBlocked
isBlocked
}
@@ -148,7 +155,7 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<
messages.add(message)
participants.forEach {
- contactsMap.put(it.rawId, it)
+ contactsMap[it.rawId] = it
}
}
@@ -173,7 +180,7 @@ fun Context.getMMSSender(msgId: Int): String {
return ""
}
-fun Context.getConversations(): ArrayList {
+fun Context.getConversations(threadId: Long? = null): ArrayList {
val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true")
val projection = arrayOf(
Threads._ID,
@@ -183,10 +190,19 @@ fun Context.getConversations(): ArrayList {
Threads.RECIPIENT_IDS
)
- val selection = "${Threads.MESSAGE_COUNT} > ?"
- val selectionArgs = arrayOf("0")
+ var selection = "${Threads.MESSAGE_COUNT} > ?"
+ var selectionArgs = arrayOf("0")
+ if (threadId != null) {
+ selection += " AND ${Threads._ID} = ?"
+ selectionArgs = arrayOf("0", threadId.toString())
+ }
+
+ val sortOrder = "${Threads.DATE} DESC"
+
val conversations = ArrayList()
- queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
+ val simpleContactHelper = SimpleContactsHelper(this)
+ val blockedNumbers = getBlockedNumbers()
+ queryCursor(uri, projection, selection, selectionArgs, sortOrder, true) { cursor ->
val id = cursor.getIntValue(Threads._ID)
var snippet = cursor.getStringValue(Threads.SNIPPET) ?: ""
if (snippet.isEmpty()) {
@@ -198,22 +214,24 @@ fun Context.getConversations(): ArrayList {
date /= 1000
}
- val read = cursor.getIntValue(Threads.READ) == 1
val rawIds = cursor.getStringValue(Threads.RECIPIENT_IDS)
val recipientIds = rawIds.split(" ").filter { it.areDigitsOnly() }.map { it.toInt() }.toMutableList()
val phoneNumbers = getThreadPhoneNumbers(recipientIds)
- if (phoneNumbers.any { isNumberBlocked(it) }) {
+ if (phoneNumbers.any { isNumberBlocked(it, blockedNumbers) }) {
return@queryCursor
}
val names = getThreadContactNames(phoneNumbers)
val title = TextUtils.join(", ", names.toTypedArray())
- val photoUri = if (phoneNumbers.size == 1) SimpleContactsHelper(this).getPhotoUriFromPhoneNumber(phoneNumbers.first()) else ""
+ val photoUri = if (phoneNumbers.size == 1) simpleContactHelper.getPhotoUriFromPhoneNumber(phoneNumbers.first()) else ""
val isGroupConversation = phoneNumbers.size > 1
- val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first())
+ val read = cursor.getIntValue(Threads.READ) == 1
+ val conversation = Conversation(null, id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first())
+
conversations.add(conversation)
}
+ conversations.sortByDescending { it.date }
return conversations
}
@@ -363,7 +381,7 @@ fun Context.getPhoneNumberFromAddressId(canonicalAddressId: Int): String {
return ""
}
-fun Context.getSuggestedContacts(): ArrayList {
+fun Context.getSuggestedContacts(privateContacts: ArrayList): ArrayList {
val contacts = ArrayList()
val uri = Sms.CONTENT_URI
val projection = arrayOf(
@@ -373,16 +391,29 @@ fun Context.getSuggestedContacts(): ArrayList {
val selection = "1 == 1) GROUP BY (${Sms.ADDRESS}"
val selectionArgs = null
val sortOrder = "${Sms.DATE} DESC LIMIT 20"
+ val blockedNumbers = getBlockedNumbers()
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor
val namePhoto = getNameAndPhotoFromPhoneNumber(senderNumber)
- if (namePhoto == null || namePhoto.name == senderNumber || isNumberBlocked(senderNumber)) {
+ var senderName = namePhoto?.name ?: ""
+ var photoUri = namePhoto?.photoUri ?: ""
+ if (namePhoto == null || isNumberBlocked(senderNumber, blockedNumbers)) {
return@queryCursor
+ } else if (namePhoto.name == senderNumber) {
+ if (privateContacts.isNotEmpty()) {
+ val privateContact = privateContacts.firstOrNull { it.phoneNumber == senderNumber }
+ if (privateContact != null) {
+ senderName = privateContact.name
+ photoUri = privateContact.photoUri
+ } else {
+ return@queryCursor
+ }
+ } else {
+ return@queryCursor
+ }
}
- val senderName = namePhoto.name
- val photoUri = namePhoto.photoUri ?: ""
val contact = SimpleContact(0, 0, senderName, photoUri, senderNumber)
if (!contacts.map { it.phoneNumber.trimToComparableNumber() }.contains(senderNumber.trimToComparableNumber())) {
contacts.add(contact)
@@ -436,14 +467,16 @@ fun Context.insertNewSMS(address: String, subject: String, body: String, date: L
return newUri?.lastPathSegment?.toInt() ?: 0
}
-fun Context.deleteConversation(id: Int) {
+fun Context.deleteConversation(threadId: Int) {
var uri = Sms.CONTENT_URI
val selection = "${Sms.THREAD_ID} = ?"
- val selectionArgs = arrayOf(id.toString())
+ val selectionArgs = arrayOf(threadId.toString())
contentResolver.delete(uri, selection, selectionArgs)
uri = Mms.CONTENT_URI
contentResolver.delete(uri, selection, selectionArgs)
+
+ conversationsDB.deleteThreadId(threadId.toLong())
}
fun Context.deleteMessage(id: Int, isMMS: Boolean) {
@@ -482,16 +515,6 @@ fun Context.getThreadId(addresses: Set): Long {
}
}
-fun Context.isNumberBlocked(number: String): Boolean {
- if (!isNougatPlus()) {
- return false
- }
-
- val blockedNumbers = getBlockedNumbers()
- val numberToCompare = number.trimToComparableNumber()
- return blockedNumbers.map { it.numberToCompare }.contains(numberToCompare) || blockedNumbers.map { it.number }.contains(numberToCompare)
-}
-
@SuppressLint("NewApi")
fun Context.showReceivedMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?, messageId: Int, isMMS: Boolean) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@@ -527,6 +550,7 @@ fun Context.showReceivedMessageNotification(address: String, body: String, threa
action = MARK_AS_READ
putExtra(MESSAGE_ID, messageId)
putExtra(MESSAGE_IS_MMS, isMMS)
+ putExtra(THREAD_ID, threadID)
}
val markAsReadPendingIntent = PendingIntent.getBroadcast(this, 0, markAsReadIntent, PendingIntent.FLAG_CANCEL_CURRENT)
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/interfaces/ConversationsDao.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/interfaces/ConversationsDao.kt
new file mode 100644
index 00000000..422245ce
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/interfaces/ConversationsDao.kt
@@ -0,0 +1,25 @@
+package com.simplemobiletools.smsmessenger.interfaces
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.simplemobiletools.smsmessenger.models.Conversation
+
+@Dao
+interface ConversationsDao {
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insertOrUpdate(conversation: Conversation): Long
+
+ @Query("SELECT * FROM conversations")
+ fun getAll(): List
+
+ @Query("UPDATE conversations SET read = 1 WHERE thread_id = :threadId")
+ fun markRead(threadId: Long)
+
+ @Query("DELETE FROM conversations WHERE id = :id")
+ fun delete(id: Long)
+
+ @Query("DELETE FROM conversations WHERE thread_id = :threadId")
+ fun deleteThreadId(threadId: Long)
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Conversation.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Conversation.kt
index 65329fc8..73e557c6 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Conversation.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Conversation.kt
@@ -1,5 +1,23 @@
package com.simplemobiletools.smsmessenger.models
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Index
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "conversations", indices = [(Index(value = ["thread_id"], unique = true))])
data class Conversation(
- val id: Int, val snippet: String, val date: Int, val read: Boolean, val title: String, val photoUri: String,
- val isGroupConversation: Boolean, val phoneNumber: String)
+ @PrimaryKey(autoGenerate = true) var id: Long?,
+ @ColumnInfo(name = "thread_id") var thread_id: Int,
+ @ColumnInfo(name = "snippet") var snippet: String,
+ @ColumnInfo(name = "date") var date: Int,
+ @ColumnInfo(name = "read") var read: Boolean,
+ @ColumnInfo(name = "title") var title: String,
+ @ColumnInfo(name = "photo_uri") var photoUri: String,
+ @ColumnInfo(name = "is_group_conversation") var isGroupConversation: Boolean,
+ @ColumnInfo(name = "phone_number") var phoneNumber: String
+) {
+ fun getStringToCompare(): String {
+ return copy(id = 0).toString()
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Message.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Message.kt
index 4285d527..e18df07c 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Message.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Message.kt
@@ -5,6 +5,6 @@ import com.simplemobiletools.commons.models.SimpleContact
data class Message(
val id: Int, val body: String, val type: Int, val participants: ArrayList, val date: Int, val read: Boolean, val thread: Int,
- val isMMS: Boolean, val attachment: MessageAttachment?, val senderName: String, val senderPhotoUri: String, val subscriptionId: Int) : ThreadItem() {
+ val isMMS: Boolean, val attachment: MessageAttachment?, var senderName: String, val senderPhotoUri: String, val subscriptionId: Int) : ThreadItem() {
fun isReceivedMessage() = type == Telephony.Sms.MESSAGE_TYPE_INBOX
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MarkAsReadReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MarkAsReadReceiver.kt
index f0cdff85..6dc79f93 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MarkAsReadReceiver.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MarkAsReadReceiver.kt
@@ -4,19 +4,27 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.simplemobiletools.commons.extensions.notificationManager
+import com.simplemobiletools.commons.helpers.ensureBackgroundThread
+import com.simplemobiletools.smsmessenger.extensions.conversationsDB
import com.simplemobiletools.smsmessenger.extensions.markMessageRead
import com.simplemobiletools.smsmessenger.helpers.MARK_AS_READ
import com.simplemobiletools.smsmessenger.helpers.MESSAGE_ID
import com.simplemobiletools.smsmessenger.helpers.MESSAGE_IS_MMS
+import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
class MarkAsReadReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
MARK_AS_READ -> {
val messageId = intent.getIntExtra(MESSAGE_ID, 0)
- val isMMS = intent.getBooleanExtra(MESSAGE_IS_MMS, false)
- context.markMessageRead(messageId, isMMS)
context.notificationManager.cancel(messageId)
+ ensureBackgroundThread {
+ val isMMS = intent.getBooleanExtra(MESSAGE_IS_MMS, false)
+ context.markMessageRead(messageId, isMMS)
+
+ val threadId = intent.getIntExtra(THREAD_ID, 0)
+ context.conversationsDB.markRead(threadId.toLong())
+ }
}
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MmsReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MmsReceiver.kt
index 865fb5a3..9cdb9a1a 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MmsReceiver.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MmsReceiver.kt
@@ -3,10 +3,12 @@ package com.simplemobiletools.smsmessenger.receivers
import android.content.Context
import android.net.Uri
import com.bumptech.glide.Glide
+import com.simplemobiletools.commons.extensions.isNumberBlocked
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.smsmessenger.R
+import com.simplemobiletools.smsmessenger.extensions.conversationsDB
+import com.simplemobiletools.smsmessenger.extensions.getConversations
import com.simplemobiletools.smsmessenger.extensions.getLatestMMS
-import com.simplemobiletools.smsmessenger.extensions.isNumberBlocked
import com.simplemobiletools.smsmessenger.extensions.showReceivedMessageNotification
// more info at https://github.com/klinker41/android-smsmms
@@ -32,6 +34,8 @@ class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() {
}
context.showReceivedMessageNotification(address, mms.body, mms.thread, glideBitmap, mms.id, true)
+ val conversation = context.getConversations(mms.thread.toLong()).firstOrNull() ?: return@ensureBackgroundThread
+ context.conversationsDB.insertOrUpdate(conversation)
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt
index 17c6f62b..a57e042a 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt
@@ -4,10 +4,9 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.provider.Telephony
-import com.simplemobiletools.smsmessenger.extensions.getThreadId
-import com.simplemobiletools.smsmessenger.extensions.insertNewSMS
-import com.simplemobiletools.smsmessenger.extensions.isNumberBlocked
-import com.simplemobiletools.smsmessenger.extensions.showReceivedMessageNotification
+import com.simplemobiletools.commons.extensions.isNumberBlocked
+import com.simplemobiletools.commons.helpers.ensureBackgroundThread
+import com.simplemobiletools.smsmessenger.extensions.*
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
class SmsReceiver : BroadcastReceiver() {
@@ -34,6 +33,11 @@ class SmsReceiver : BroadcastReceiver() {
val messageId = context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
context.showReceivedMessageNotification(address, body, threadId.toInt(), null, messageId, false)
refreshMessages()
+
+ ensureBackgroundThread {
+ val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread
+ context.conversationsDB.insertOrUpdate(conversation)
+ }
}
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/services/HeadlessSmsSendService.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/services/HeadlessSmsSendService.kt
index c5cbb81b..eaeba8fb 100644
--- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/services/HeadlessSmsSendService.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/services/HeadlessSmsSendService.kt
@@ -2,6 +2,7 @@ package com.simplemobiletools.smsmessenger.services
import android.app.Service
import android.content.Intent
+import android.net.Uri
import com.klinker.android.send_message.Settings
import com.klinker.android.send_message.Transaction
import com.simplemobiletools.smsmessenger.extensions.getThreadId
@@ -15,7 +16,7 @@ class HeadlessSmsSendService : Service() {
return START_NOT_STICKY
}
- val number = intent.dataString!!.removePrefix("sms:").removePrefix("smsto:").removePrefix("mms").removePrefix("mmsto:").trim()
+ val number = Uri.decode(intent.dataString!!.removePrefix("sms:").removePrefix("smsto:").removePrefix("mms").removePrefix("mmsto:").trim())
val text = intent.getStringExtra(Intent.EXTRA_TEXT)
val settings = Settings()
settings.useSystemSending = true
diff --git a/app/src/main/res/layout/activity_conversation.xml b/app/src/main/res/layout/activity_new_conversation.xml
similarity index 100%
rename from app/src/main/res/layout/activity_conversation.xml
rename to app/src/main/res/layout/activity_new_conversation.xml
index 79704e5d..f76e807f 100644
--- a/app/src/main/res/layout/activity_conversation.xml
+++ b/app/src/main/res/layout/activity_new_conversation.xml
@@ -42,12 +42,12 @@
android:id="@+id/no_contacts_placeholder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_below="@+id/message_divider_two"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/bigger_margin"
android:alpha="0.8"
android:gravity="center"
android:paddingLeft="@dimen/activity_margin"
- android:layout_below="@+id/message_divider_two"
android:paddingRight="@dimen/activity_margin"
android:text="@string/no_access_to_contacts"
android:textSize="@dimen/bigger_text_size"
diff --git a/app/src/main/res/layout/activity_thread.xml b/app/src/main/res/layout/activity_thread.xml
index 3978a078..33ec03ed 100644
--- a/app/src/main/res/layout/activity_thread.xml
+++ b/app/src/main/res/layout/activity_thread.xml
@@ -161,6 +161,7 @@
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:hint="@string/type_a_message"
+ android:inputType="textCapSentences|textMultiLine"
android:minHeight="@dimen/normal_icon_size" />
+
-
+ Schlichter SMS Messenger
+ SMS Messenger
+ schreibe eine Nachricht…
+ Nachricht wurde noch nicht gesendet
+ füge eine Person hinzu
+ Anhang
+ keine gespeicherten Chats gefunden
+ einen neuen Chat beginnen
+
+
+ neuer Chat
+ Füge einen Kontakt oder eine Nummer hinzu…
+ Vorschläge
+
+
+ Empfangene SMS
+ neue Nachricht
+ markiere als gelesen
+ Mark as Unread
+
+
+ Möchtest du wirklich alle Nachrichten dieses Chat löschen?
+
+
+
+
- %d Chat
+ - %d Chats
+
+
+
+
+ - %d Nachricht
+ - %d Nachrichten
+
+
+
+ Why does the app require access to the internet?
+ Sadly it is needed for sending MMS attachments. Not being able to send MMS would be a really huge disadvantage compared to other apps, so we decided to go this way.
+ However, as usually, there are no ads, tracking or analytics whatsoever, the internet is used only for sending MMS.
+
+
+
+ Simple SMS Messenger - Manage messages easily
+
+ An easy and quick way of managing SMS and MMS messages without ads.
+
+ A great way to stay in touch with your relatives, by sending both SMS and MMS messages. The app properly handles group messaging too, just like blocking numbers from Android 7+.
+
+ It offers many date formats to choose from, to make you feel comfortable at using it. You can toggle between 12 and 24 hours time format too.
+
+ It has a really tiny app size compared to the competition, making it really fast to download.
+
+ It comes with material design and dark theme by default, provides great user experience for easy usage. The lack of internet access gives you more privacy, security and stability than other apps.
+
+ Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
+
+ Check out the full suite of Simple Tools here:
+ https://www.simplemobiletools.com
+
+ Facebook:
+ https://www.facebook.com/simplemobiletools
+
+ Reddit:
+ https://www.reddit.com/r/SimpleMobileTools
+
+
+
+
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
new file mode 100644
index 00000000..a10ddb94
--- /dev/null
+++ b/app/src/main/res/values-el/strings.xml
@@ -0,0 +1,72 @@
+
+ Απλός SMS Messenger
+ SMS Messenger
+ Πληκτρολογήστε ένα μήνυμα…
+ Το μήνυμα δεν έχει σταλεί.
+ Προσθήκη ατόμου
+ Συνημμένο
+ Δεν βρέθηκαν αποθηκευμένες συνομιλίες
+ Έναρξη συνομιλίας
+
+
+ Νέα συνομιλία
+ Προσθήκη επαφής ή αριθμού…
+ Προτάσεις
+
+
+ Ελήφθη SMS
+ Νέο μήνυμα
+ Σήμανση ως αναγνωσμένου
+ Mark as Unread
+
+
+ Είστε βέβαιοι ότι θέλετε να διαγράψετε όλα τα μηνύματα αυτής της συνομιλίας;
+
+
+
+ - %d συνομιλία
+ - %d συνομιλίες
+
+
+
+
+ - %d μήνυμα
+ - %d μηνύματα
+
+
+
+ Γιατί η εφαρμογή απαιτεί πρόσβαση στο Internet;
+ Δυστυχώς, απαιτείται για την αποστολή συνημμένων MMS. Το να μην είμαστε σε θέση να στείλουμε MMS θα αποτελούσε πραγματικά τεράστιο μειονέκτημα σε σύγκριση με άλλες εφαρμογές, επομένως αποφασίσαμε να ακολουθήσουμε αυτόν τον δρόμο.
+ Ωστόσο, όπως συνήθως, δεν υπάρχουν καθόλου διαφημίσεις, παρακολούθηση ή αναλύσεις, το διαδίκτυο χρησιμοποιείται μόνο για την αποστολή MMS.
+
+
+
+ Απλός SMS Messenger - Εύκολη διαχείριση μηνυμάτων
+
+ Ένας εύκολος και γρήγορος τρόπος διαχείρισης SMS και MMS χωρίς διαφημίσεις.
+
+ Ένας εξαιρετικός τρόπος για να μείνετε σε επαφή με τους συγγενείς σας, στέλνοντας μηνύματα SMS και MMS. Η εφαρμογή χειρίζεται σωστά και την ομαδική ανταλλαγή μηνυμάτων, όπως και τον αποκλεισμό αριθμών από Android 7+.
+
+ Προσφέρει πολλές μορφές ημερομηνίας για να διαλέξετε, ώστε να αισθάνεστε άνετα να τις χρησιμοποιήσετε. Μπορείτε επίσης να κάνετε εναλλαγή μεταξύ 12 και 24 ωρών.
+
+ Έχει πραγματικά πολύ μικρό μέγεθος εφαρμογής σε σύγκριση με τον ανταγωνισμό, καθιστώντας την πραγματικά γρήγορη στη λήψη της.
+
+ Διατίθεται με σχεδίαση υλικού και σκούρο θέμα από προεπιλογή, παρέχει εξαιρετική εμπειρία χρήστη για εύκολη χρήση. Η έλλειψη πρόσβασης στο διαδίκτυο σάς παρέχει περισσότερο απόρρητο, ασφάλεια και σταθερότητα από ό,τι άλλες εφαρμογές.
+
+ Δεν περιέχει διαφημίσεις ή περιττά δικαιώματα. Είναι πλήρως ανοιχτού κώδικα, παρέχει προσαρμόσιμα χρώματα.
+
+ Δείτε την πλήρη σειρά Simple Tools here:
+ https://www.simplemobiletools.com
+
+ Facebook:
+ https://www.facebook.com/simplemobiletools
+
+ Reddit:
+ https://www.reddit.com/r/SimpleMobileTools
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
new file mode 100644
index 00000000..ea852951
--- /dev/null
+++ b/app/src/main/res/values-es/strings.xml
@@ -0,0 +1,72 @@
+
+ Mensajería SMS Simple
+ Mensajería SMS
+ Escribe un mensaje…
+ El mensaje no se ha enviado.
+ Añadir persona
+ Archivo adjunto
+ No se han encontrado conversaciones
+ Inicia una conversación
+
+
+ Nueva conversación
+ Escribe contacto o número…
+ Sugerencias
+
+
+ Mensaje recibico
+ Nuevo mensaje
+ Marcar como leído
+ Mark as Unread
+
+
+ ¿Estás seguro que quieres eliminar todos los mensajes en esta conversación?
+
+
+
+ - %d conversación
+ - %d conversaciones
+
+
+
+
+ - %d mensaje
+ - %d mensajes
+
+
+
+ ¿Por qué la aplicación requiere acceso a internet?
+ Tristemente es necesitado apra 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.
+ Sin embargo, como siempre, no hay anuncios, rastreo o análisis, por lo que el internet solo es usado para enviar MMS.
+
+
+
+ Mensajería SMS Simple - Mensajea fácilmente
+
+ Una forma fácil y rápida de enviar mensajes SMS y MMS sin anuncios.
+
+ Una excelente forma de estar en contacto con tus parientes, enviando mensajes SMS y MMS. La aplicación maneja adecuadamente mensajes grupales también, así como números bloqueados desde Android 7+.
+
+ Ofrece varios formatos de fecha para escoger, para hacerte sentir cómodo mientras la usas. También puedes escoger entre formato de 12 y 24 horas.
+
+ La aplicaicón pesa muy poco comparada con la competencia, haciendo que se descargue realmente rápido.
+
+ Viene con diseño material y tema obscuro por defecto, proveyendo una excelente experiencia de usuario y un uso fácil. La falta de acceso a internet te da más privacidad, seguridad y estabilidad que otras aplicaciones.
+
+ No contiene anuncios o permisos innecesarios. Es completamente de código abierto, provee colores personalizables.
+
+ Mira la suite completa de herramientas Simples aquí:
+ https://www.simplemobiletools.com
+
+ Facebook:
+ https://www.facebook.com/simplemobiletools
+
+ Reddit:
+ https://www.reddit.com/r/SimpleMobileTools
+
+
+
+
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 3418233a..492e988f 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -17,6 +17,7 @@
Gautos žinutės
Nauja žinutė
Mark as Read
+ Mark as Unread
Ar tikrai norite ištrinti visas šio pokalbio žinutes?
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
new file mode 100644
index 00000000..ef0e03e4
--- /dev/null
+++ b/app/src/main/res/values-nl/strings.xml
@@ -0,0 +1,72 @@
+
+ Eenvoudig Berichtenbeheer (SMS)
+ Berichten
+ Typ een bericht…
+ Bericht niet verzonden.
+ Persoon toevoegen
+ Bijlage
+ Geen opgeslagen berichten gevonden
+ Een gesprek starten
+
+
+ Nieuw gesprek
+ Contact of nummer toevoegen…
+ Suggesties
+
+
+ Ontvangen berichten
+ Nieuw bericht
+ Als gelezen markeren
+ Als ongelezen markeren
+
+
+ Alle berichten in dit gesprek verwijderen?
+
+
+
+ - %d gesperk
+ - %d gesprekken
+
+
+
+
+ - %d bericht
+ - %d berichten
+
+
+
+ Waarom heeft deze app toegang nodig tot het internet?
+ Dit is helaas nodig voor het verzenden van MMS-bijlagen. Het versturen van MMS-berichten onmogelijk maken zou een te groot nadeel t.o.v. andere apps betekenen en daarom hebben we besloten om het toch toe te voegen.
+ Zoals gewoonlijk bevat de app echter geen advertenties, tracking of analytics; de verbinding wordt alleen maar gebruikt voor het versturen van MMS-berichten.
+
+
+
+ Eenvoudig Berichtenbeheer - Verstuur snel SMS/MMS
+
+ Beheer eenvoudig en snel SMS- en MMS-berichten, zonder advertenties.
+
+ Blijf gemakkelijk in contact met familieleden en vrienden door het versuren van SMS- en MMS-berichten. De app ondersteunt groepsberichten, evenals het blokkeren van nummers (vanaf Android 7).
+
+ Het datumformaat kan worden ingesteld naar voorkeur. Ook kan geschakeld worden tussen 12- of 24-uursnotatie.
+
+ Vergeleken met de competitie is deze app zeer compact en dus erg snel gedownload.
+
+ De app is ontworpen volgens material design en heeft standaard een donker thema. De app heeft geen toegang tot het internet nodig en voorziet van meer privacy, veiligheid en stabiliteit dan andere apps.
+
+ Bevat geen advertenties of onnodige machtigingen. Volledig open-source. Kleuren van de app kunnen worden aangepast.
+
+ Probeer ook eens de andere apps van Simple Tools:
+ https://www.simplemobiletools.com
+
+ Facebook:
+ https://www.facebook.com/simplemobiletools
+
+ Reddit:
+ https://www.reddit.com/r/SimpleMobileTools
+
+
+
+
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index e26b0c63..c1f5c471 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -16,7 +16,8 @@
SMS recebida
Nova mensagem
- Mark as Read
+ Marcar como lida
+ Mark as Unread
Tem a certeza de que deseja eliminar todas as mensagens desta conversa?
@@ -54,7 +55,7 @@
Não contém anúncios nem permissões desnecessárias. É open source e permite a personalização de cores.
- Consulte o conjunto completo de aplicações Simple aqui:
+ Consulte o conjunto completo de aplicações Simple Tools aqui:
https://www.simplemobiletools.com
Facebook:
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 55549886..95e6b2e2 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -17,6 +17,7 @@
Получено SMS
Новое сообщение
Пометить прочитанным
+ Mark as Unread
Вы уверены, что хотите удалить все сообщения в этой переписке?
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 81fbe50b..cdbef4e7 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -17,6 +17,7 @@
Prijatá SMS
Nová správa
Označiť ako prečítané
+ Označiť ako neprečítané
Ste si istý, že chcete odstrániť všetky správy tejto konverzácie?
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a476612e..35038243 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -17,6 +17,7 @@
Received SMS
New message
Mark as Read
+ Mark as Unread
Are you sure you want to delete all messages of this conversation?
diff --git a/fastlane/metadata/android/el/full_description.txt b/fastlane/metadata/android/el/full_description.txt
new file mode 100644
index 00000000..00238e7b
--- /dev/null
+++ b/fastlane/metadata/android/el/full_description.txt
@@ -0,0 +1,18 @@
+Ένας εξαιρετικός τρόπος για να μείνετε σε επαφή με τους συγγενείς σας, στέλνοντας μηνύματα SMS και MMS. Η εφαρμογή χειρίζεται σωστά και την ομαδική ανταλλαγή μηνυμάτων, όπως και τον αποκλεισμό αριθμών από Android 7+.
+
+Προσφέρει πολλές μορφές ημερομηνίας για να διαλέξετε, ώστε να αισθάνεστε άνετα να τις χρησιμοποιήσετε. Μπορείτε επίσης να κάνετε εναλλαγή μεταξύ 12 και 24 ωρών.
+
+Έχει πραγματικά πολύ μικρό μέγεθος εφαρμογής σε σύγκριση με τον ανταγωνισμό, καθιστώντας την πραγματικά γρήγορη στη λήψη της.
+
+Διατίθεται με σχεδίαση υλικού και σκούρο θέμα από προεπιλογή, παρέχει εξαιρετική εμπειρία χρήστη για εύκολη χρήση. Η έλλειψη πρόσβασης στο διαδίκτυο σάς παρέχει περισσότερο απόρρητο, ασφάλεια και σταθερότητα από ό,τι άλλες εφαρμογές.
+
+Δεν περιέχει διαφημίσεις ή περιττά δικαιώματα. Είναι πλήρως ανοιχτού κώδικα, παρέχει προσαρμόσιμα χρώματα.
+
+Δείτε την πλήρη σειρά Simple Tools here:
+https://www.simplemobiletools.com
+
+Facebook:
+https://www.facebook.com/simplemobiletools
+
+Reddit:
+https://www.reddit.com/r/SimpleMobileTools
diff --git a/fastlane/metadata/android/el/short_description.txt b/fastlane/metadata/android/el/short_description.txt
new file mode 100644
index 00000000..b5c05944
--- /dev/null
+++ b/fastlane/metadata/android/el/short_description.txt
@@ -0,0 +1 @@
+Ένας εύκολος και γρήγορος τρόπος διαχείρισης SMS και MMS χωρίς διαφημίσεις.
diff --git a/fastlane/metadata/android/el/title.txt b/fastlane/metadata/android/el/title.txt
new file mode 100644
index 00000000..a9e7f5bb
--- /dev/null
+++ b/fastlane/metadata/android/el/title.txt
@@ -0,0 +1 @@
+Απλός SMS Messenger - Εύκολη διαχείριση μηνυμάτων
diff --git a/fastlane/metadata/android/en-US/changelogs/6.txt b/fastlane/metadata/android/en-US/changelogs/6.txt
new file mode 100644
index 00000000..a5e1385f
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/6.txt
@@ -0,0 +1,3 @@
+ * Adding support for accessing private contacts stored in Simple Contacts Pro (to be added soon)
+ * Make sure the conversations are sorted properly
+ * Added some other UI and translation improvements
diff --git a/fastlane/metadata/android/en-US/changelogs/7.txt b/fastlane/metadata/android/en-US/changelogs/7.txt
new file mode 100644
index 00000000..58162fbd
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/7.txt
@@ -0,0 +1,4 @@
+ * Improved the performance at checking if a number is blocked
+ * Use capital letters at sentences at writing a message
+ * Allow blocking numbers from the main screen
+ * Fixed a glitch with adding new people to a conversation
diff --git a/fastlane/metadata/android/en-US/changelogs/9.txt b/fastlane/metadata/android/en-US/changelogs/9.txt
new file mode 100644
index 00000000..e8b55377
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/9.txt
@@ -0,0 +1,4 @@
+ * Cache the conversations shown on the main screen to drastically improve their loading speed
+ * Allow adding newlines with the Enter button
+ * Properly handle a new View third party intent
+ * Added some other translation, UI and stability improvements
diff --git a/fastlane/metadata/android/es/full_description.txt b/fastlane/metadata/android/es/full_description.txt
new file mode 100644
index 00000000..0c808159
--- /dev/null
+++ b/fastlane/metadata/android/es/full_description.txt
@@ -0,0 +1,18 @@
+Una excelente forma de estar en contacto con tus parientes, enviando mensajes SMS y MMS. La aplicación maneja adecuadamente mensajes grupales también, así como números bloqueados desde Android 7+.
+
+Ofrece varios formatos de fecha para escoger, para hacerte sentir cómodo mientras la usas. También puedes escoger entre formato de 12 y 24 horas.
+
+La aplicaicón pesa muy poco comparada con la competencia, haciendo que se descargue realmente rápido.
+
+Viene con diseño material y tema obscuro por defecto, proveyendo una excelente experiencia de usuario y un uso fácil. La falta de acceso a internet te da más privacidad, seguridad y estabilidad que otras aplicaciones.
+
+No contiene anuncios o permisos innecesarios. Es completamente de código abierto, provee colores personalizables.
+
+Mira la suite completa de herramientas Simples aquí:
+https://www.simplemobiletools.com
+
+Facebook:
+https://www.facebook.com/simplemobiletools
+
+Reddit:
+https://www.reddit.com/r/SimpleMobileTools
diff --git a/fastlane/metadata/android/es/short_description.txt b/fastlane/metadata/android/es/short_description.txt
new file mode 100644
index 00000000..af5b0127
--- /dev/null
+++ b/fastlane/metadata/android/es/short_description.txt
@@ -0,0 +1 @@
+Una forma fácil y rápida de enviar mensajes SMS y MMS sin anuncios.
diff --git a/fastlane/metadata/android/es/title.txt b/fastlane/metadata/android/es/title.txt
new file mode 100644
index 00000000..f5aab8d1
--- /dev/null
+++ b/fastlane/metadata/android/es/title.txt
@@ -0,0 +1 @@
+Mensajería SMS Simple - Mensajea fácilmente
diff --git a/fastlane/metadata/android/pt/full_description.txt b/fastlane/metadata/android/pt/full_description.txt
index 9f3741ff..88af0785 100644
--- a/fastlane/metadata/android/pt/full_description.txt
+++ b/fastlane/metadata/android/pt/full_description.txt
@@ -8,7 +8,7 @@ Disponibiliza um design atrativo e um tema escuro por omissão. A não utilizaç
Não contém anúncios nem permissões desnecessárias. É open source e permite a personalização de cores.
-Consulte o conjunto completo de aplicações Simple aqui:
+Consulte o conjunto completo de aplicações Simple Tools aqui:
https://www.simplemobiletools.com
Facebook: