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 Get it on Google Play +Get it on F-Droid
App image 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: