Merge pull request #5 from SimpleMobileTools/master

upd
This commit is contained in:
solokot
2020-11-04 17:44:25 +03:00
committed by GitHub
51 changed files with 765 additions and 130 deletions

View File

@@ -1,6 +1,47 @@
Changelog Changelog
========== ==========
Version 5.5.0 *(2020-11-04)*
----------------------------
* Allow dialing or copying selected conversation phone numbers
* Allow copying specific parts of messages into clipboard
* Adding an option to show character counter at outgoing messages
* Couple other UI, translation and stability improvements
Version 5.4.5 *(2020-10-27)*
----------------------------
* Fixed some smaller glitches + translation improvements
Version 5.4.4 *(2020-09-23)*
----------------------------
* Fixing a crash at receiving MMS
Version 5.4.3 *(2020-09-23)*
----------------------------
* Fixing a crash at receiving MMS
Version 5.4.2 *(2020-09-22)*
----------------------------
* Fix incoming message notifications sometimes not being shown
Version 5.4.1 *(2020-08-23)*
----------------------------
* Show a tick at successfully sent messages
* Properly show the sender name at incoming messages, if sent by a private contact
Version 5.4.0 *(2020-07-27)*
----------------------------
* Show unread message badges on some devices that support it
* Added some improvements related to handling private contacts
* Other stability, UI and translation improvements
Version 5.3.0 *(2020-06-18)* Version 5.3.0 *(2020-06-18)*
---------------------------- ----------------------------

View File

@@ -10,15 +10,14 @@ if (keystorePropertiesFile.exists()) {
} }
android { android {
compileSdkVersion 29 compileSdkVersion 30
buildToolsVersion "29.0.3"
defaultConfig { defaultConfig {
applicationId "com.simplemobiletools.smsmessenger" applicationId "com.simplemobiletools.smsmessenger"
minSdkVersion 22 minSdkVersion 22
targetSdkVersion 29 targetSdkVersion 30
versionCode 10 versionCode 18
versionName "5.3.0" versionName "5.5.0"
setProperty("archivesBaseName", "sms-messenger") setProperty("archivesBaseName", "sms-messenger")
} }
@@ -57,10 +56,11 @@ android {
} }
dependencies { dependencies {
implementation 'com.simplemobiletools:commons:5.29.7' implementation 'com.simplemobiletools:commons:5.31.23'
implementation 'org.greenrobot:eventbus:3.2.0' implementation 'org.greenrobot:eventbus:3.2.0'
implementation 'com.klinkerapps:android-smsmms:5.2.6' implementation 'com.klinkerapps:android-smsmms:5.2.6'
implementation 'com.github.tibbi:IndicatorFastScroll:08f512858a' implementation 'com.github.tibbi:IndicatorFastScroll:08f512858a'
implementation "me.leolin:ShortcutBadger:1.1.22"
kapt "androidx.room:room-compiler:2.2.5" kapt "androidx.room:room-compiler:2.2.5"
implementation "androidx.room:room-runtime:2.2.5" implementation "androidx.room:room-runtime:2.2.5"

View File

@@ -21,6 +21,7 @@ import com.simplemobiletools.smsmessenger.adapters.ConversationsAdapter
import com.simplemobiletools.smsmessenger.extensions.config import com.simplemobiletools.smsmessenger.extensions.config
import com.simplemobiletools.smsmessenger.extensions.conversationsDB import com.simplemobiletools.smsmessenger.extensions.conversationsDB
import com.simplemobiletools.smsmessenger.extensions.getConversations import com.simplemobiletools.smsmessenger.extensions.getConversations
import com.simplemobiletools.smsmessenger.extensions.updateUnreadCountBadge
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE
import com.simplemobiletools.smsmessenger.models.Conversation import com.simplemobiletools.smsmessenger.models.Conversation
@@ -137,7 +138,10 @@ class MainActivity : SimpleActivity() {
handlePermission(PERMISSION_READ_CONTACTS) { handlePermission(PERMISSION_READ_CONTACTS) {
initMessenger() initMessenger()
bus = EventBus.getDefault() bus = EventBus.getDefault()
bus!!.register(this) try {
bus!!.register(this)
} catch (e: Exception) {
}
} }
} else { } else {
finish() finish()
@@ -164,7 +168,13 @@ class MainActivity : SimpleActivity() {
private fun getCachedConversations() { private fun getCachedConversations() {
ensureBackgroundThread { ensureBackgroundThread {
val conversations = conversationsDB.getAll().sortedByDescending { it.date }.toMutableList() as ArrayList<Conversation> val conversations = try {
conversationsDB.getAll().sortedByDescending { it.date }.toMutableList() as ArrayList<Conversation>
} catch (e: Exception) {
ArrayList()
}
updateUnreadCountBadge(conversations)
runOnUiThread { runOnUiThread {
setupConversations(conversations) setupConversations(conversations)
getNewConversations(conversations) getNewConversations(conversations)
@@ -173,7 +183,7 @@ class MainActivity : SimpleActivity() {
} }
private fun getNewConversations(cachedConversations: ArrayList<Conversation>) { private fun getNewConversations(cachedConversations: ArrayList<Conversation>) {
val privateCursor = getMyContactsContentProviderCursorLoader().loadInBackground() val privateCursor = getMyContactsCursor().loadInBackground()
ensureBackgroundThread { ensureBackgroundThread {
val conversations = getConversations() val conversations = getConversations()
@@ -181,9 +191,11 @@ class MainActivity : SimpleActivity() {
val privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor) val privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
if (privateContacts.isNotEmpty()) { if (privateContacts.isNotEmpty()) {
conversations.filter { it.title == it.phoneNumber }.forEach { conversation -> conversations.filter { it.title == it.phoneNumber }.forEach { conversation ->
privateContacts.firstOrNull { it.phoneNumber == conversation.phoneNumber }?.apply { privateContacts.forEach { contact ->
conversation.title = name if (contact.doesContainPhoneNumber(conversation.phoneNumber)) {
conversation.photoUri = photoUri conversation.title = contact.name
conversation.photoUri = contact.photoUri
}
} }
} }
} }
@@ -232,7 +244,10 @@ class MainActivity : SimpleActivity() {
conversations_list.adapter = this conversations_list.adapter = this
} }
} else { } else {
(currAdapter as ConversationsAdapter).updateConversations(conversations) try {
(currAdapter as ConversationsAdapter).updateConversations(conversations)
} catch (ignored: Exception) {
}
} }
} }
@@ -283,7 +298,8 @@ class MainActivity : SimpleActivity() {
val faqItems = arrayListOf( val faqItems = arrayListOf(
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons), FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons),
FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons) FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons),
FAQItem(R.string.faq_9_title_commons, R.string.faq_9_text_commons)
) )
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true) startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)

View File

@@ -5,6 +5,7 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.WindowManager import android.view.WindowManager
import com.google.gson.Gson
import com.reddit.indicatorfastscroll.FastScrollItemIndicator import com.reddit.indicatorfastscroll.FastScrollItemIndicator
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.MyContactsContentProvider import com.simplemobiletools.commons.helpers.MyContactsContentProvider
@@ -64,7 +65,7 @@ class NewConversationActivity : SimpleActivity() {
val searchString = it val searchString = it
val filteredContacts = ArrayList<SimpleContact>() val filteredContacts = ArrayList<SimpleContact>()
allContacts.forEach { allContacts.forEach {
if (it.phoneNumber.contains(searchString, true) || it.name.contains(searchString, true)) { if (it.phoneNumbers.any { it.contains(searchString, true) } || it.name.contains(searchString, true)) {
filteredContacts.add(it) filteredContacts.add(it)
} }
} }
@@ -134,7 +135,7 @@ class NewConversationActivity : SimpleActivity() {
ContactsAdapter(this, contacts, contacts_list, null) { ContactsAdapter(this, contacts, contacts_list, null) {
hideKeyboard() hideKeyboard()
launchThreadActivity((it as SimpleContact).phoneNumber, it.name) launchThreadActivity((it as SimpleContact).phoneNumbers.first(), it.name)
}.apply { }.apply {
contacts_list.adapter = this contacts_list.adapter = this
} }
@@ -143,7 +144,7 @@ class NewConversationActivity : SimpleActivity() {
} }
private fun fillSuggestedContacts(callback: () -> Unit) { private fun fillSuggestedContacts(callback: () -> Unit) {
val privateCursor = getMyContactsContentProviderCursorLoader().loadInBackground() val privateCursor = getMyContactsCursor().loadInBackground()
ensureBackgroundThread { ensureBackgroundThread {
privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor) privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
val suggestions = getSuggestedContacts(privateContacts) val suggestions = getSuggestedContacts(privateContacts)
@@ -159,10 +160,14 @@ class NewConversationActivity : SimpleActivity() {
val contact = it val contact = it
layoutInflater.inflate(R.layout.item_suggested_contact, null).apply { layoutInflater.inflate(R.layout.item_suggested_contact, null).apply {
suggested_contact_name.text = contact.name suggested_contact_name.text = contact.name
SimpleContactsHelper(this@NewConversationActivity).loadContactImage(contact.photoUri, suggested_contact_image, contact.name) suggested_contact_name.setTextColor(baseConfig.textColor)
suggestions_holder.addView(this)
setOnClickListener { if (!isDestroyed) {
launchThreadActivity(contact.phoneNumber, contact.name) SimpleContactsHelper(this@NewConversationActivity).loadContactImage(contact.photoUri, suggested_contact_image, contact.name)
suggestions_holder.addView(this)
setOnClickListener {
launchThreadActivity(contact.phoneNumbers.first(), contact.name)
}
} }
} }
} }
@@ -186,11 +191,13 @@ class NewConversationActivity : SimpleActivity() {
private fun launchThreadActivity(phoneNumber: String, name: String) { private fun launchThreadActivity(phoneNumber: String, name: String) {
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: "" val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
val numbers = phoneNumber.split(";").toSet()
val number = if (numbers.size == 1) phoneNumber else Gson().toJson(numbers)
Intent(this, ThreadActivity::class.java).apply { Intent(this, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, getThreadId(phoneNumber).toInt()) putExtra(THREAD_ID, getThreadId(numbers).toInt())
putExtra(THREAD_TITLE, name) putExtra(THREAD_TITLE, name)
putExtra(THREAD_TEXT, text) putExtra(THREAD_TEXT, text)
putExtra(THREAD_NUMBER, phoneNumber) putExtra(THREAD_NUMBER, number)
if (intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) { if (intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)

View File

@@ -34,6 +34,7 @@ class SettingsActivity : SimpleActivity() {
setupManageBlockedNumbers() setupManageBlockedNumbers()
setupChangeDateTimeFormat() setupChangeDateTimeFormat()
setupFontSize() setupFontSize()
setupShowCharacterCounter()
updateTextColors(settings_scrollview) updateTextColors(settings_scrollview)
if (blockedNumbersAtPause != -1 && blockedNumbersAtPause != getBlockedNumbers().hashCode()) { if (blockedNumbersAtPause != -1 && blockedNumbersAtPause != getBlockedNumbers().hashCode()) {
@@ -106,4 +107,12 @@ class SettingsActivity : SimpleActivity() {
} }
} }
} }
private fun setupShowCharacterCounter() {
settings_show_character_counter.isChecked = config.showCharacterCounter
settings_show_character_counter_holder.setOnClickListener {
settings_show_character_counter.toggle()
config.showCharacterCounter = settings_show_character_counter.isChecked
}
}
} }

View File

@@ -11,6 +11,7 @@ import android.os.Bundle
import android.provider.Telephony import android.provider.Telephony
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
import android.text.TextUtils import android.text.TextUtils
import android.util.TypedValue
import android.view.* import android.view.*
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.LinearLayout import android.widget.LinearLayout
@@ -51,6 +52,7 @@ class ThreadActivity : SimpleActivity() {
private var threadId = 0 private var threadId = 0
private var currentSIMCardIndex = 0 private var currentSIMCardIndex = 0
private var isActivityVisible = false
private var threadItems = ArrayList<ThreadItem>() private var threadItems = ArrayList<ThreadItem>()
private var bus: EventBus? = null private var bus: EventBus? = null
private var participants = ArrayList<SimpleContact>() private var participants = ArrayList<SimpleContact>()
@@ -86,8 +88,18 @@ class ThreadActivity : SimpleActivity() {
} }
} }
override fun onResume() {
super.onResume()
isActivityVisible = true
}
override fun onPause() {
super.onPause()
isActivityVisible = false
}
private fun setupThread() { private fun setupThread() {
val privateCursor = getMyContactsContentProviderCursorLoader().loadInBackground() val privateCursor = getMyContactsCursor().loadInBackground()
ensureBackgroundThread { ensureBackgroundThread {
messages = getMessages(threadId) messages = getMessages(threadId)
participants = if (messages.isEmpty()) { participants = if (messages.isEmpty()) {
@@ -100,9 +112,9 @@ class ThreadActivity : SimpleActivity() {
privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor) privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
if (privateContacts.isNotEmpty()) { if (privateContacts.isNotEmpty()) {
val senderNumbersToReplace = HashMap<String, String>() val senderNumbersToReplace = HashMap<String, String>()
participants.filter { it.name == it.phoneNumber }.forEach { participant -> participants.filter { it.doesContainPhoneNumber(it.name) }.forEach { participant ->
privateContacts.firstOrNull { it.phoneNumber == participant.phoneNumber }?.apply { privateContacts.firstOrNull { it.doesContainPhoneNumber(participant.phoneNumbers.first()) }?.apply {
senderNumbersToReplace[participant.phoneNumber] = name senderNumbersToReplace[participant.phoneNumbers.first()] = name
participant.name = name participant.name = name
participant.photoUri = photoUri participant.photoUri = photoUri
} }
@@ -124,7 +136,7 @@ class ThreadActivity : SimpleActivity() {
return@ensureBackgroundThread return@ensureBackgroundThread
} }
val contact = SimpleContact(0, 0, name, "", number) val contact = SimpleContact(0, 0, name, "", arrayListOf(number), ArrayList(), ArrayList())
participants.add(contact) participants.add(contact)
} }
@@ -140,8 +152,8 @@ class ThreadActivity : SimpleActivity() {
} else if (it.mimetype.startsWith("video/")) { } else if (it.mimetype.startsWith("video/")) {
val metaRetriever = MediaMetadataRetriever() val metaRetriever = MediaMetadataRetriever()
metaRetriever.setDataSource(this, it.uri) metaRetriever.setDataSource(this, it.uri)
it.width = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH).toInt() it.width = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt()
it.height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt() it.height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
} }
if (it.width < 0) { if (it.width < 0) {
@@ -241,7 +253,7 @@ class ThreadActivity : SimpleActivity() {
confirm_inserted_number?.setOnClickListener { confirm_inserted_number?.setOnClickListener {
val number = add_contact_or_number.value val number = add_contact_or_number.value
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", number) val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", arrayListOf(number), ArrayList(), ArrayList())
addSelectedContact(contact) addSelectedContact(contact)
} }
} }
@@ -253,6 +265,10 @@ class ThreadActivity : SimpleActivity() {
confirm_manage_contacts.applyColorFilter(textColor) confirm_manage_contacts.applyColorFilter(textColor)
thread_add_attachment.applyColorFilter(textColor) thread_add_attachment.applyColorFilter(textColor)
thread_character_counter.beVisibleIf(config.showCharacterCounter)
thread_character_counter.setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize())
thread_type_message.setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize())
thread_send_message.setOnClickListener { thread_send_message.setOnClickListener {
sendMessage() sendMessage()
} }
@@ -260,13 +276,20 @@ class ThreadActivity : SimpleActivity() {
thread_send_message.isClickable = false thread_send_message.isClickable = false
thread_type_message.onTextChangeListener { thread_type_message.onTextChangeListener {
checkSendMessageAvailability() checkSendMessageAvailability()
thread_character_counter.text = it.length.toString()
} }
confirm_manage_contacts.setOnClickListener { confirm_manage_contacts.setOnClickListener {
hideKeyboard() hideKeyboard()
thread_add_contacts.beGone() thread_add_contacts.beGone()
val numbers = participants.map { it.phoneNumber }.toSet() val numbers = HashSet<String>()
participants.forEach {
it.phoneNumbers.forEach {
numbers.add(it)
}
}
val newThreadId = getThreadId(numbers).toInt() val newThreadId = getThreadId(numbers).toInt()
if (threadId != newThreadId) { if (threadId != newThreadId) {
Intent(this, ThreadActivity::class.java).apply { Intent(this, ThreadActivity::class.java).apply {
@@ -297,7 +320,7 @@ class ThreadActivity : SimpleActivity() {
val availableSIMs = SubscriptionManager.from(this).activeSubscriptionInfoList ?: return val availableSIMs = SubscriptionManager.from(this).activeSubscriptionInfoList ?: return
if (availableSIMs.size > 1) { if (availableSIMs.size > 1) {
availableSIMs.forEachIndexed { index, subscriptionInfo -> availableSIMs.forEachIndexed { index, subscriptionInfo ->
var label = subscriptionInfo.displayName.toString() var label = subscriptionInfo.displayName?.toString() ?: ""
if (subscriptionInfo.number?.isNotEmpty() == true) { if (subscriptionInfo.number?.isNotEmpty() == true) {
label += " (${subscriptionInfo.number})" label += " (${subscriptionInfo.number})"
} }
@@ -305,7 +328,13 @@ class ThreadActivity : SimpleActivity() {
availableSIMCards.add(SIMCard) availableSIMCards.add(SIMCard)
} }
val numbers = participants.map { it.phoneNumber }.toTypedArray() val numbers = ArrayList<String>()
participants.forEach {
it.phoneNumbers.forEach {
numbers.add(it)
}
}
currentSIMCardIndex = availableSIMs.indexOfFirstOrNull { it.subscriptionId == config.getUseSIMIdAtNumber(numbers.first()) } ?: 0 currentSIMCardIndex = availableSIMs.indexOfFirstOrNull { it.subscriptionId == config.getUseSIMIdAtNumber(numbers.first()) } ?: 0
thread_select_sim_icon.applyColorFilter(config.textColor) thread_select_sim_icon.applyColorFilter(config.textColor)
@@ -327,7 +356,13 @@ class ThreadActivity : SimpleActivity() {
} }
private fun blockNumber() { private fun blockNumber() {
val numbers = participants.map { it.phoneNumber } val numbers = ArrayList<String>()
participants.forEach {
it.phoneNumbers.forEach {
numbers.add(it)
}
}
val numbersString = TextUtils.join(", ", numbers) val numbersString = TextUtils.join(", ", numbers)
val question = String.format(resources.getString(R.string.block_confirmation), numbersString) val question = String.format(resources.getString(R.string.block_confirmation), numbersString)
@@ -421,24 +456,30 @@ class ThreadActivity : SimpleActivity() {
var prevDateTime = 0 var prevDateTime = 0
var hadUnreadItems = false var hadUnreadItems = false
messages.forEach { val cnt = messages.size
for (i in 0 until cnt) {
val message = messages[i]
// do not show the date/time above every message, only if the difference between the 2 messages is at least MIN_DATE_TIME_DIFF_SECS // do not show the date/time above every message, only if the difference between the 2 messages is at least MIN_DATE_TIME_DIFF_SECS
if (it.date - prevDateTime > MIN_DATE_TIME_DIFF_SECS) { if (message.date - prevDateTime > MIN_DATE_TIME_DIFF_SECS) {
val simCardID = subscriptionIdToSimId[it.subscriptionId] ?: "?" val simCardID = subscriptionIdToSimId[message.subscriptionId] ?: "?"
items.add(ThreadDateTime(it.date, simCardID)) items.add(ThreadDateTime(message.date, simCardID))
prevDateTime = it.date prevDateTime = message.date
} }
items.add(it) items.add(message)
if (it.type == Telephony.Sms.MESSAGE_TYPE_FAILED) { if (message.type == Telephony.Sms.MESSAGE_TYPE_FAILED) {
items.add(ThreadError(it.id)) items.add(ThreadError(message.id))
} }
if (!it.read) { if (!message.read) {
hadUnreadItems = true hadUnreadItems = true
markMessageRead(it.id, it.isMMS) markMessageRead(message.id, message.isMMS)
conversationsDB.markRead(threadId.toLong()) conversationsDB.markRead(threadId.toLong())
} }
if (i == cnt - 1 && message.type == Telephony.Sms.MESSAGE_TYPE_SENT) {
items.add(ThreadSuccess(message.id))
}
} }
if (hadUnreadItems) { if (hadUnreadItems) {
@@ -518,7 +559,13 @@ class ThreadActivity : SimpleActivity() {
return return
} }
val numbers = participants.map { it.phoneNumber }.toTypedArray() val numbers = ArrayList<String>()
participants.forEach {
it.phoneNumbers.forEach {
numbers.add(it)
}
}
val settings = Settings() val settings = Settings()
settings.useSystemSending = true settings.useSystemSending = true
@@ -531,7 +578,7 @@ class ThreadActivity : SimpleActivity() {
} }
val transaction = Transaction(this, settings) val transaction = Transaction(this, settings)
val message = com.klinker.android.send_message.Message(msg, numbers) val message = com.klinker.android.send_message.Message(msg, numbers.toTypedArray())
if (attachmentUris.isNotEmpty()) { if (attachmentUris.isNotEmpty()) {
for (uri in attachmentUris) { for (uri in attachmentUris) {
@@ -613,7 +660,10 @@ class ThreadActivity : SimpleActivity() {
@Subscribe(threadMode = ThreadMode.ASYNC) @Subscribe(threadMode = ThreadMode.ASYNC)
fun refreshMessages(event: Events.RefreshMessages) { fun refreshMessages(event: Events.RefreshMessages) {
notificationManager.cancel(threadId) if (isActivityVisible) {
notificationManager.cancel(threadId)
}
messages = getMessages(threadId) messages = getMessages(threadId)
setupAdapter() setupAdapter()
} }

View File

@@ -31,7 +31,7 @@ class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: Ar
} }
findViewById<TextView>(R.id.item_contact_name).text = contact.name findViewById<TextView>(R.id.item_contact_name).text = contact.name
findViewById<TextView>(R.id.item_contact_number).text = contact.phoneNumber findViewById<TextView>(R.id.item_contact_number).text = contact.phoneNumbers.first()
SimpleContactsHelper(context).loadContactImage(contact.photoUri, findViewById(R.id.item_contact_image), contact.name) SimpleContactsHelper(context).loadContactImage(contact.photoUri, findViewById(R.id.item_contact_image), contact.name)
} }
@@ -46,7 +46,7 @@ class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: Ar
resultList.clear() resultList.clear()
val searchString = constraint.toString().normalizeString() val searchString = constraint.toString().normalizeString()
contacts.forEach { contacts.forEach {
if (it.phoneNumber.contains(searchString, true) || it.name.contains(searchString, true)) { if (it.doesContainPhoneNumber(searchString) || it.name.contains(searchString, true)) {
resultList.add(it) resultList.add(it)
} }
} }

View File

@@ -67,7 +67,7 @@ class ContactsAdapter(activity: SimpleActivity, var contacts: ArrayList<SimpleCo
} }
findViewById<TextView>(R.id.item_contact_number).apply { findViewById<TextView>(R.id.item_contact_number).apply {
text = contact.phoneNumber text = contact.phoneNumbers.first()
setTextColor(textColor) setTextColor(textColor)
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize) setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
} }

View File

@@ -2,6 +2,7 @@ package com.simplemobiletools.smsmessenger.adapters
import android.content.Intent import android.content.Intent
import android.graphics.Typeface import android.graphics.Typeface
import android.net.Uri
import android.text.TextUtils import android.text.TextUtils
import android.util.TypedValue import android.util.TypedValue
import android.view.Menu import android.view.Menu
@@ -37,8 +38,10 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
override fun prepareActionMode(menu: Menu) { override fun prepareActionMode(menu: Menu) {
menu.apply { menu.apply {
findItem(R.id.cab_add_number_to_contact).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
findItem(R.id.cab_block_number).isVisible = isNougatPlus() findItem(R.id.cab_block_number).isVisible = isNougatPlus()
findItem(R.id.cab_add_number_to_contact).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
findItem(R.id.cab_dial_number).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
findItem(R.id.cab_copy_number).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
} }
} }
@@ -50,7 +53,8 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
when (id) { when (id) {
R.id.cab_add_number_to_contact -> addNumberToContact() R.id.cab_add_number_to_contact -> addNumberToContact()
R.id.cab_block_number -> askConfirmBlock() R.id.cab_block_number -> askConfirmBlock()
R.id.cab_select_all -> selectAll() R.id.cab_dial_number -> dialNumber()
R.id.cab_copy_number -> copyNumberToClipboard()
R.id.cab_delete -> askConfirmDelete() R.id.cab_delete -> askConfirmDelete()
} }
} }
@@ -110,6 +114,26 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
} }
} }
private fun dialNumber() {
val conversation = getSelectedItems().firstOrNull() ?: return
Intent(Intent.ACTION_DIAL).apply {
data = Uri.fromParts("tel", conversation.phoneNumber, null)
if (resolveActivity(activity.packageManager) != null) {
activity.startActivity(this)
finishActMode()
} else {
activity.toast(R.string.no_app_found)
}
}
}
private fun copyNumberToClipboard() {
val conversation = getSelectedItems().firstOrNull() ?: return
activity.copyToClipboard(conversation.phoneNumber)
finishActMode()
}
private fun askConfirmDelete() { private fun askConfirmDelete() {
val itemsCnt = selectedKeys.size val itemsCnt = selectedKeys.size
val items = resources.getQuantityString(R.plurals.delete_conversations, itemsCnt, itemsCnt) val items = resources.getQuantityString(R.plurals.delete_conversations, itemsCnt, itemsCnt)
@@ -135,7 +159,11 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
activity.deleteConversation(it.thread_id) activity.deleteConversation(it.thread_id)
activity.notificationManager.cancel(it.thread_id) activity.notificationManager.cancel(it.thread_id)
} }
conversations.removeAll(conversationsToRemove)
try {
conversations.removeAll(conversationsToRemove)
} catch (ignored: Exception) {
}
activity.runOnUiThread { activity.runOnUiThread {
if (conversationsToRemove.isEmpty()) { if (conversationsToRemove.isEmpty()) {

View File

@@ -29,17 +29,17 @@ import com.simplemobiletools.commons.views.FastScroller
import com.simplemobiletools.commons.views.MyRecyclerView import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.activities.SimpleActivity import com.simplemobiletools.smsmessenger.activities.SimpleActivity
import com.simplemobiletools.smsmessenger.dialogs.SelectTextDialog
import com.simplemobiletools.smsmessenger.extensions.deleteMessage import com.simplemobiletools.smsmessenger.extensions.deleteMessage
import com.simplemobiletools.smsmessenger.helpers.* import com.simplemobiletools.smsmessenger.helpers.*
import com.simplemobiletools.smsmessenger.models.Message import com.simplemobiletools.smsmessenger.models.*
import com.simplemobiletools.smsmessenger.models.ThreadDateTime
import com.simplemobiletools.smsmessenger.models.ThreadError
import com.simplemobiletools.smsmessenger.models.ThreadItem
import kotlinx.android.synthetic.main.item_attachment_image.view.* import kotlinx.android.synthetic.main.item_attachment_image.view.*
import kotlinx.android.synthetic.main.item_received_message.view.* import kotlinx.android.synthetic.main.item_received_message.view.*
import kotlinx.android.synthetic.main.item_received_unknown_attachment.view.* import kotlinx.android.synthetic.main.item_received_unknown_attachment.view.*
import kotlinx.android.synthetic.main.item_sent_unknown_attachment.view.* import kotlinx.android.synthetic.main.item_sent_unknown_attachment.view.*
import kotlinx.android.synthetic.main.item_thread_date_time.view.* import kotlinx.android.synthetic.main.item_thread_date_time.view.*
import kotlinx.android.synthetic.main.item_thread_error.view.*
import kotlinx.android.synthetic.main.item_thread_success.view.*
class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem>, recyclerView: MyRecyclerView, fastScroller: FastScroller, class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem>, recyclerView: MyRecyclerView, fastScroller: FastScroller,
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) { itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
@@ -60,6 +60,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
menu.apply { menu.apply {
findItem(R.id.cab_copy_to_clipboard).isVisible = isOneItemSelected findItem(R.id.cab_copy_to_clipboard).isVisible = isOneItemSelected
findItem(R.id.cab_share).isVisible = isOneItemSelected findItem(R.id.cab_share).isVisible = isOneItemSelected
findItem(R.id.cab_select_text).isVisible = isOneItemSelected
} }
} }
@@ -71,7 +72,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
when (id) { when (id) {
R.id.cab_copy_to_clipboard -> copyToClipboard() R.id.cab_copy_to_clipboard -> copyToClipboard()
R.id.cab_share -> shareText() R.id.cab_share -> shareText()
R.id.cab_select_all -> selectAll() R.id.cab_select_text -> selectText()
R.id.cab_delete -> askConfirmDelete() R.id.cab_delete -> askConfirmDelete()
} }
} }
@@ -93,6 +94,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
THREAD_DATE_TIME -> R.layout.item_thread_date_time THREAD_DATE_TIME -> R.layout.item_thread_date_time
THREAD_RECEIVED_MESSAGE -> R.layout.item_received_message THREAD_RECEIVED_MESSAGE -> R.layout.item_received_message
THREAD_SENT_MESSAGE_ERROR -> R.layout.item_thread_error THREAD_SENT_MESSAGE_ERROR -> R.layout.item_thread_error
THREAD_SENT_MESSAGE_SUCCESS -> R.layout.item_thread_success
else -> R.layout.item_sent_message else -> R.layout.item_sent_message
} }
return createViewHolder(layout, parent) return createViewHolder(layout, parent)
@@ -101,10 +103,11 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = messages[position] val item = messages[position]
holder.bindView(item, true, item is Message) { itemView, layoutPosition -> holder.bindView(item, true, item is Message) { itemView, layoutPosition ->
if (item is ThreadDateTime) { when (item) {
setupDateTime(itemView, item) is ThreadDateTime -> setupDateTime(itemView, item)
} else if (item !is ThreadError) { is ThreadSuccess -> setupThreadSuccess(itemView)
setupView(itemView, item as Message) is ThreadError -> setupThreadError(itemView)
else -> setupView(itemView, item as Message)
} }
} }
bindViewHolder(holder) bindViewHolder(holder)
@@ -118,6 +121,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
item is ThreadDateTime -> THREAD_DATE_TIME item is ThreadDateTime -> THREAD_DATE_TIME
(messages[position] as? Message)?.isReceivedMessage() == true -> THREAD_RECEIVED_MESSAGE (messages[position] as? Message)?.isReceivedMessage() == true -> THREAD_RECEIVED_MESSAGE
item is ThreadError -> THREAD_SENT_MESSAGE_ERROR item is ThreadError -> THREAD_SENT_MESSAGE_ERROR
item is ThreadSuccess -> THREAD_SENT_MESSAGE_SUCCESS
else -> THREAD_SENT_MESSAGE else -> THREAD_SENT_MESSAGE
} }
} }
@@ -132,6 +136,13 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
activity.shareTextIntent(firstItem.body) activity.shareTextIntent(firstItem.body)
} }
private fun selectText() {
val firstItem = getSelectedItems().first() as? Message ?: return
if (firstItem.body.trim().isNotEmpty()) {
SelectTextDialog(activity, firstItem.body)
}
}
private fun askConfirmDelete() { private fun askConfirmDelete() {
val itemsCnt = selectedKeys.size val itemsCnt = selectedKeys.size
val items = resources.getQuantityString(R.plurals.delete_messages, itemsCnt, itemsCnt) val items = resources.getQuantityString(R.plurals.delete_messages, itemsCnt, itemsCnt)
@@ -309,4 +320,12 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
} }
} }
} }
private fun setupThreadSuccess(view: View) {
view.thread_success.applyColorFilter(textColor)
}
private fun setupThreadError(view: View) {
view.thread_error.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
}
} }

View File

@@ -0,0 +1,22 @@
package com.simplemobiletools.smsmessenger.dialogs
import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.smsmessenger.R
import kotlinx.android.synthetic.main.dialog_select_text.view.*
// helper dialog for selecting just a part of a message, not copying the whole into clipboard
class SelectTextDialog(val activity: BaseSimpleActivity, val text: String) {
init {
val view = activity.layoutInflater.inflate(R.layout.dialog_select_text, null).apply {
dialog_select_text_value.text = text
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok) { dialog, which -> { } }
.create().apply {
activity.setupDialogStuff(view, this)
}
}
}

View File

@@ -13,6 +13,8 @@ import android.media.AudioAttributes
import android.media.AudioManager import android.media.AudioManager
import android.media.RingtoneManager import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.provider.ContactsContract.PhoneLookup import android.provider.ContactsContract.PhoneLookup
import android.provider.Telephony.* import android.provider.Telephony.*
import android.text.TextUtils import android.text.TextUtils
@@ -29,6 +31,7 @@ import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
import com.simplemobiletools.smsmessenger.models.* import com.simplemobiletools.smsmessenger.models.*
import com.simplemobiletools.smsmessenger.receivers.DirectReplyReceiver import com.simplemobiletools.smsmessenger.receivers.DirectReplyReceiver
import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver
import me.leolin.shortcutbadger.ShortcutBadger
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@@ -83,7 +86,7 @@ fun Context.getMessages(threadId: Int): ArrayList<Message> {
val read = cursor.getIntValue(Sms.READ) == 1 val read = cursor.getIntValue(Sms.READ) == 1
val thread = cursor.getIntValue(Sms.THREAD_ID) val thread = cursor.getIntValue(Sms.THREAD_ID)
val subscriptionId = cursor.getIntValue(Sms.SUBSCRIPTION_ID) val subscriptionId = cursor.getIntValue(Sms.SUBSCRIPTION_ID)
val participant = SimpleContact(0, 0, senderName, photoUri, senderNumber) val participant = SimpleContact(0, 0, senderName, photoUri, arrayListOf(senderNumber), ArrayList(), ArrayList())
val isMMS = false val isMMS = false
val message = Message(id, body, type, arrayListOf(participant), date, read, thread, isMMS, null, senderName, photoUri, subscriptionId) val message = Message(id, body, type, arrayListOf(participant), date, read, thread, isMMS, null, senderName, photoUri, subscriptionId)
messages.add(message) messages.add(message)
@@ -334,7 +337,7 @@ fun Context.getThreadParticipants(threadId: Int, contactsMap: HashMap<Int, Simpl
val namePhoto = getNameAndPhotoFromPhoneNumber(phoneNumber) val namePhoto = getNameAndPhotoFromPhoneNumber(phoneNumber)
val name = namePhoto?.name ?: "" val name = namePhoto?.name ?: ""
val photoUri = namePhoto?.photoUri ?: "" val photoUri = namePhoto?.photoUri ?: ""
val contact = SimpleContact(addressId, addressId, name, photoUri, phoneNumber) val contact = SimpleContact(addressId, addressId, name, photoUri, arrayListOf(phoneNumber), ArrayList(), ArrayList())
participants.add(contact) participants.add(contact)
} }
} }
@@ -403,7 +406,7 @@ fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): Arr
return@queryCursor return@queryCursor
} else if (namePhoto.name == senderNumber) { } else if (namePhoto.name == senderNumber) {
if (privateContacts.isNotEmpty()) { if (privateContacts.isNotEmpty()) {
val privateContact = privateContacts.firstOrNull { it.phoneNumber == senderNumber } val privateContact = privateContacts.firstOrNull { it.phoneNumbers.first() == senderNumber }
if (privateContact != null) { if (privateContact != null) {
senderName = privateContact.name senderName = privateContact.name
photoUri = privateContact.photoUri photoUri = privateContact.photoUri
@@ -415,8 +418,8 @@ fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): Arr
} }
} }
val contact = SimpleContact(0, 0, senderName, photoUri, senderNumber) val contact = SimpleContact(0, 0, senderName, photoUri, arrayListOf(senderNumber), ArrayList(), ArrayList())
if (!contacts.map { it.phoneNumber.trimToComparableNumber() }.contains(senderNumber.trimToComparableNumber())) { if (!contacts.map { it.phoneNumbers.first().trimToComparableNumber() }.contains(senderNumber.trimToComparableNumber())) {
contacts.add(contact) contacts.add(contact)
} }
} }
@@ -472,7 +475,11 @@ fun Context.deleteConversation(threadId: Int) {
var uri = Sms.CONTENT_URI var uri = Sms.CONTENT_URI
val selection = "${Sms.THREAD_ID} = ?" val selection = "${Sms.THREAD_ID} = ?"
val selectionArgs = arrayOf(threadId.toString()) val selectionArgs = arrayOf(threadId.toString())
contentResolver.delete(uri, selection, selectionArgs) try {
contentResolver.delete(uri, selection, selectionArgs)
} catch (e: Exception) {
showErrorToast(e)
}
uri = Mms.CONTENT_URI uri = Mms.CONTENT_URI
contentResolver.delete(uri, selection, selectionArgs) contentResolver.delete(uri, selection, selectionArgs)
@@ -522,6 +529,15 @@ fun Context.markThreadMessagesUnread(threadId: Int) {
} }
} }
fun Context.updateUnreadCountBadge(conversations: List<Conversation>) {
val unreadCount = conversations.count { !it.read }
if (unreadCount == 0) {
ShortcutBadger.removeCount(this)
} else {
ShortcutBadger.applyCount(this, unreadCount)
}
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
fun Context.getThreadId(address: String): Long { fun Context.getThreadId(address: String): Long {
return if (isMarshmallowPlus()) { return if (isMarshmallowPlus()) {
@@ -548,8 +564,23 @@ fun Context.getThreadId(addresses: Set<String>): Long {
} }
} }
@SuppressLint("NewApi")
fun Context.showReceivedMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?) { fun Context.showReceivedMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?) {
val privateCursor = getMyContactsCursor().loadInBackground()
ensureBackgroundThread {
var sender = getNameAndPhotoFromPhoneNumber(address)?.name ?: ""
if (address == sender) {
val privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
sender = privateContacts.firstOrNull { it.doesContainPhoneNumber(address) }?.name ?: address
}
Handler(Looper.getMainLooper()).post {
showMessageNotification(address, body, threadID, bitmap, sender)
}
}
}
@SuppressLint("NewApi")
fun Context.showMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?, sender: String) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
if (isOreoPlus()) { if (isOreoPlus()) {
@@ -576,8 +607,6 @@ fun Context.showReceivedMessageNotification(address: String, body: String, threa
val pendingIntent = PendingIntent.getActivity(this, threadID, intent, PendingIntent.FLAG_UPDATE_CURRENT) val pendingIntent = PendingIntent.getActivity(this, threadID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val summaryText = getString(R.string.new_message) val summaryText = getString(R.string.new_message)
val sender = getNameAndPhotoFromPhoneNumber(address)?.name ?: ""
val markAsReadIntent = Intent(this, MarkAsReadReceiver::class.java).apply { val markAsReadIntent = Intent(this, MarkAsReadReceiver::class.java).apply {
action = MARK_AS_READ action = MARK_AS_READ
putExtra(THREAD_ID, threadID) putExtra(THREAD_ID, threadID)
@@ -617,12 +646,11 @@ fun Context.showReceivedMessageNotification(address: String, body: String, threa
.setCategory(Notification.CATEGORY_MESSAGE) .setCategory(Notification.CATEGORY_MESSAGE)
.setAutoCancel(true) .setAutoCancel(true)
.setSound(soundUri, AudioManager.STREAM_NOTIFICATION) .setSound(soundUri, AudioManager.STREAM_NOTIFICATION)
.addAction(R.drawable.ic_check_vector, getString(R.string.mark_as_read), markAsReadPendingIntent)
.setChannelId(NOTIFICATION_CHANNEL)
if (replyAction != null) { if (replyAction != null) {
builder.addAction(replyAction) builder.addAction(replyAction)
} }
builder.addAction(R.drawable.ic_check_vector, getString(R.string.mark_as_read), markAsReadPendingIntent)
.setChannelId(NOTIFICATION_CHANNEL)
notificationManager.notify(threadID, builder.build()) notificationManager.notify(threadID, builder.build())
} }

View File

@@ -13,4 +13,8 @@ class Config(context: Context) : BaseConfig(context) {
} }
fun getUseSIMIdAtNumber(number: String) = prefs.getInt(USE_SIM_ID_PREFIX + number, 0) fun getUseSIMIdAtNumber(number: String) = prefs.getInt(USE_SIM_ID_PREFIX + number, 0)
var showCharacterCounter: Boolean
get() = prefs.getBoolean(SHOW_CHARACTER_COUNTER, false)
set(showCharacterCounter) = prefs.edit().putBoolean(SHOW_CHARACTER_COUNTER, showCharacterCounter).apply()
} }

View File

@@ -11,6 +11,7 @@ const val THREAD_ATTACHMENT_URI = "thread_attachment_uri"
const val THREAD_ATTACHMENT_URIS = "thread_attachment_uris" const val THREAD_ATTACHMENT_URIS = "thread_attachment_uris"
const val USE_SIM_ID_PREFIX = "use_sim_id_" const val USE_SIM_ID_PREFIX = "use_sim_id_"
const val NOTIFICATION_CHANNEL = "simple_sms_messenger" const val NOTIFICATION_CHANNEL = "simple_sms_messenger"
const val SHOW_CHARACTER_COUNTER = "show_character_counter"
private const val PATH = "com.simplemobiletools.smsmessenger.action." private const val PATH = "com.simplemobiletools.smsmessenger.action."
const val MARK_AS_READ = PATH + "mark_as_read" const val MARK_AS_READ = PATH + "mark_as_read"
@@ -21,6 +22,7 @@ const val THREAD_DATE_TIME = 1
const val THREAD_RECEIVED_MESSAGE = 2 const val THREAD_RECEIVED_MESSAGE = 2
const val THREAD_SENT_MESSAGE = 3 const val THREAD_SENT_MESSAGE = 3
const val THREAD_SENT_MESSAGE_ERROR = 4 const val THREAD_SENT_MESSAGE_ERROR = 4
const val THREAD_SENT_MESSAGE_SUCCESS = 5
fun refreshMessages() { fun refreshMessages() {
EventBus.getDefault().post(Events.RefreshMessages()) EventBus.getDefault().post(Events.RefreshMessages())

View File

@@ -14,6 +14,9 @@ interface ConversationsDao {
@Query("SELECT * FROM conversations") @Query("SELECT * FROM conversations")
fun getAll(): List<Conversation> fun getAll(): List<Conversation>
@Query("SELECT * FROM conversations WHERE read = 0")
fun getUnreadConversations(): List<Conversation>
@Query("UPDATE conversations SET read = 1 WHERE thread_id = :threadId") @Query("UPDATE conversations SET read = 1 WHERE thread_id = :threadId")
fun markRead(threadId: Long) fun markRead(threadId: Long)

View File

@@ -0,0 +1,4 @@
package com.simplemobiletools.smsmessenger.models
// show a check after the latest message, if it is a sent one and succeeded
data class ThreadSuccess(val messageID: Int) : ThreadItem()

View File

@@ -7,6 +7,7 @@ import com.simplemobiletools.commons.extensions.notificationManager
import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.smsmessenger.extensions.conversationsDB import com.simplemobiletools.smsmessenger.extensions.conversationsDB
import com.simplemobiletools.smsmessenger.extensions.markThreadMessagesRead import com.simplemobiletools.smsmessenger.extensions.markThreadMessagesRead
import com.simplemobiletools.smsmessenger.extensions.updateUnreadCountBadge
import com.simplemobiletools.smsmessenger.helpers.MARK_AS_READ import com.simplemobiletools.smsmessenger.helpers.MARK_AS_READ
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
@@ -19,6 +20,7 @@ class MarkAsReadReceiver : BroadcastReceiver() {
ensureBackgroundThread { ensureBackgroundThread {
context.markThreadMessagesRead(threadId) context.markThreadMessagesRead(threadId)
context.conversationsDB.markRead(threadId.toLong()) context.conversationsDB.markRead(threadId.toLong())
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
} }
} }
} }

View File

@@ -2,20 +2,19 @@ package com.simplemobiletools.smsmessenger.receivers
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Handler
import android.os.Looper
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.simplemobiletools.commons.extensions.isNumberBlocked import com.simplemobiletools.commons.extensions.isNumberBlocked
import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.extensions.conversationsDB import com.simplemobiletools.smsmessenger.extensions.*
import com.simplemobiletools.smsmessenger.extensions.getConversations
import com.simplemobiletools.smsmessenger.extensions.getLatestMMS
import com.simplemobiletools.smsmessenger.extensions.showReceivedMessageNotification
// more info at https://github.com/klinker41/android-smsmms // more info at https://github.com/klinker41/android-smsmms
class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() { class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() {
override fun onMessageReceived(context: Context, messageUri: Uri) { override fun onMessageReceived(context: Context, messageUri: Uri) {
val mms = context.getLatestMMS() ?: return val mms = context.getLatestMMS() ?: return
val address = mms.participants.firstOrNull()?.phoneNumber ?: "" val address = mms.participants.firstOrNull()?.phoneNumbers?.first() ?: ""
if (context.isNumberBlocked(address)) { if (context.isNumberBlocked(address)) {
return return
} }
@@ -33,9 +32,14 @@ class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() {
null null
} }
context.showReceivedMessageNotification(address, mms.body, mms.thread, glideBitmap) Handler(Looper.getMainLooper()).post {
val conversation = context.getConversations(mms.thread.toLong()).firstOrNull() ?: return@ensureBackgroundThread context.showReceivedMessageNotification(address, mms.body, mms.thread, glideBitmap)
context.conversationsDB.insertOrUpdate(conversation) val conversation = context.getConversations(mms.thread.toLong()).firstOrNull() ?: return@post
ensureBackgroundThread {
context.conversationsDB.insertOrUpdate(conversation)
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
}
}
} }
} }

View File

@@ -30,14 +30,16 @@ class SmsReceiver : BroadcastReceiver() {
} }
if (!context.isNumberBlocked(address)) { if (!context.isNumberBlocked(address)) {
context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
context.showReceivedMessageNotification(address, body, threadId.toInt(), null)
refreshMessages()
ensureBackgroundThread { ensureBackgroundThread {
context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread
context.conversationsDB.insertOrUpdate(conversation) context.conversationsDB.insertOrUpdate(conversation)
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
} }
context.showReceivedMessageNotification(address, body, threadId.toInt(), null)
refreshMessages()
} }
} }
} }

View File

@@ -149,5 +149,28 @@
android:clickable="false" /> android:clickable="false" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/settings_show_character_counter_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingStart="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin"
android:paddingEnd="@dimen/normal_margin"
android:paddingBottom="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_show_character_counter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:paddingStart="@dimen/medium_margin"
android:text="@string/show_character_counter"
app:switchPadding="@dimen/medium_margin" />
</RelativeLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View File

@@ -66,6 +66,7 @@
android:layout_marginStart="@dimen/medium_margin" android:layout_marginStart="@dimen/medium_margin"
android:layout_marginEnd="@dimen/medium_margin" android:layout_marginEnd="@dimen/medium_margin"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/confirm_selection"
android:paddingStart="@dimen/medium_margin" android:paddingStart="@dimen/medium_margin"
android:paddingEnd="@dimen/medium_margin" android:paddingEnd="@dimen/medium_margin"
android:src="@drawable/ic_check_vector" android:src="@drawable/ic_check_vector"
@@ -125,6 +126,7 @@
android:layout_marginEnd="@dimen/small_margin" android:layout_marginEnd="@dimen/small_margin"
android:alpha="0.9" android:alpha="0.9"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/attachment"
android:padding="@dimen/normal_margin" android:padding="@dimen/normal_margin"
android:src="@drawable/ic_plus_vector" /> android:src="@drawable/ic_plus_vector" />
@@ -166,14 +168,17 @@
<ImageView <ImageView
android:id="@+id/thread_select_sim_icon" android:id="@+id/thread_select_sim_icon"
android:layout_width="@dimen/normal_icon_size" android:layout_width="wrap_content"
android:layout_height="@dimen/normal_icon_size" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_marginTop="@dimen/small_margin" android:layout_marginTop="@dimen/small_margin"
android:layout_toStartOf="@+id/thread_send_message" android:layout_toStartOf="@+id/thread_character_counter"
android:alpha="0.9" android:alpha="0.9"
android:background="?selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
android:padding="@dimen/normal_margin" android:paddingStart="@dimen/medium_margin"
android:paddingTop="@dimen/normal_margin"
android:paddingEnd="@dimen/medium_margin"
android:paddingBottom="@dimen/normal_margin"
android:src="@drawable/ic_sim_vector" android:src="@drawable/ic_sim_vector"
android:visibility="gone" /> android:visibility="gone" />
@@ -190,6 +195,21 @@
android:visibility="gone" android:visibility="gone"
tools:text="1" /> tools:text="1" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/thread_character_counter"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignTop="@+id/thread_send_message"
android:layout_alignBottom="@+id/thread_send_message"
android:layout_toStartOf="@+id/thread_send_message"
android:gravity="center"
android:paddingStart="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin"
android:text="0"
android:textSize="@dimen/normal_text_size"
android:visibility="gone"
tools:ignore="HardcodedText" />
<ImageView <ImageView
android:id="@+id/thread_send_message" android:id="@+id/thread_send_message"
android:layout_width="@dimen/normal_icon_size" android:layout_width="@dimen/normal_icon_size"
@@ -201,6 +221,7 @@
android:alpha="0.4" android:alpha="0.4"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:clickable="false" android:clickable="false"
android:contentDescription="@string/ok"
android:padding="@dimen/medium_margin" android:padding="@dimen/medium_margin"
android:src="@drawable/ic_send_vector" /> android:src="@drawable/ic_send_vector" />

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<com.simplemobiletools.commons.views.MyTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/dialog_select_text_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/big_margin"
android:paddingTop="@dimen/big_margin"
android:paddingEnd="@dimen/big_margin"
android:textIsSelectable="true"
android:textSize="@dimen/big_text_size"
tools:text="My sample text" />

View File

@@ -18,7 +18,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.7"> app:layout_constraintWidth_percent="0.8">
<ImageView <ImageView
android:id="@+id/thread_message_sender_photo" android:id="@+id/thread_message_sender_photo"

View File

@@ -18,7 +18,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.7"> app:layout_constraintWidth_percent="0.8">
<LinearLayout <LinearLayout
android:id="@+id/thread_mesage_attachments_holder" android:id="@+id/thread_mesage_attachments_holder"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/thread_date_time" android:id="@+id/thread_error"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="@dimen/activity_margin">
<ImageView
android:id="@+id/thread_success"
android:layout_width="@dimen/activity_margin"
android:layout_height="@dimen/activity_margin"
android:src="@drawable/ic_check_vector" />
</RelativeLayout>

View File

@@ -12,10 +12,14 @@
android:title="@string/block_number" android:title="@string/block_number"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/cab_select_all" android:id="@+id/cab_dial_number"
android:icon="@drawable/ic_select_all_vector" android:icon="@drawable/ic_phone_vector"
android:title="@string/select_all" android:title="@string/dial_number"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/cab_copy_number"
android:title="@string/copy_number_to_clipboard"
app:showAsAction="never" />
<item <item
android:id="@+id/cab_delete" android:id="@+id/cab_delete"
android:icon="@drawable/ic_delete_vector" android:icon="@drawable/ic_delete_vector"

View File

@@ -12,10 +12,9 @@
android:title="@string/share" android:title="@string/share"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/cab_select_all" android:id="@+id/cab_select_text"
android:icon="@drawable/ic_select_all_vector" android:title="@string/select_text"
android:title="@string/select_all" app:showAsAction="never" />
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/cab_delete" android:id="@+id/cab_delete"
android:icon="@drawable/ic_delete_vector" android:icon="@drawable/ic_delete_vector"

View File

@@ -2,26 +2,27 @@
<string name="app_name">Schlichter SMS Messenger</string> <string name="app_name">Schlichter SMS Messenger</string>
<string name="app_launcher_name">SMS Messenger</string> <string name="app_launcher_name">SMS Messenger</string>
<string name="type_a_message">schreibe eine Nachricht…</string> <string name="type_a_message">schreibe eine Nachricht…</string>
<string name="message_not_sent">Nachricht wurde noch nicht gesendet</string> <string name="message_not_sent">Nachricht wurde nicht gesendet.</string>
<string name="add_person">füge eine Person hinzu</string> <string name="add_person">Person hinzufügen</string>
<string name="attachment">Anhang</string> <string name="attachment">Anhang</string>
<string name="no_conversations_found">keine gespeicherten Chats gefunden</string> <string name="no_conversations_found">Keine gespeicherten Chats gefunden</string>
<string name="start_conversation">einen neuen Chat beginnen</string> <string name="start_conversation">Neuen Chat beginnen</string>
<string name="reply">Reply</string> <string name="reply">Antworten</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">neuer Chat</string> <string name="new_conversation">Neuer Chat</string>
<string name="add_contact_or_number">Füge einen Kontakt oder eine Nummer hinzu…</string> <string name="add_contact_or_number">Kontakt oder Nummer hinzufügen</string>
<string name="suggestions">Vorschläge</string> <string name="suggestions">Vorschläge</string>
<!-- Notifications --> <!-- Notifications -->
<string name="channel_received_sms">Empfangene SMS</string> <string name="channel_received_sms">Empfangene SMS</string>
<string name="new_message">neue Nachricht</string> <string name="new_message">Neue Nachricht</string>
<string name="mark_as_read">markiere als gelesen</string> <string name="mark_as_read">Als gelesen markieren</string>
<string name="mark_as_unread">Mark as Unread</string> <string name="mark_as_unread">Als ungelesen markieren</string>
<!-- Confirmation dialog --> <!-- Confirmation dialog -->
<string name="delete_whole_conversation_confirmation">Möchtest du wirklich alle Nachrichten dieses Chat löschen?</string> <string name="delete_whole_conversation_confirmation">Möchtest du wirklich alle Nachrichten dieses Chats löschen?</string>
<!-- Are you sure you want to delete 5 conversations? --> <!-- Are you sure you want to delete 5 conversations? -->
<plurals name="delete_conversations"> <plurals name="delete_conversations">
@@ -36,9 +37,9 @@
</plurals> </plurals>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Why does the app require access to the internet?</string> <string name="faq_1_title">Warum benötigt diese App Internetzugriff?</string>
<string name="faq_1_text">Sadly it is needed for sending MMS attachments. Not being able to send MMS would be a really huge disadvantage compared to other apps, so we decided to go this way. <string name="faq_1_text">Leider ist dies nötig, um MMS-Anhänge zu versenden. Es wäre ein großer Nachteil gegenüber anderen Apps, wenn keine MMS versendet werden könnten, also haben wir uns für diesen Weg entschieden.
However, as usually, there are no ads, tracking or analytics whatsoever, the internet is used only for sending MMS.</string> Jedoch gibt es wie immer keine Werbung, Tracking oder Analytics, der Internetzugriff wird also nur für das Versenden von MMS verwendet.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it --> <!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->

View File

@@ -7,7 +7,8 @@
<string name="attachment">Συνημμένο</string> <string name="attachment">Συνημμένο</string>
<string name="no_conversations_found">Δεν βρέθηκαν αποθηκευμένες συνομιλίες</string> <string name="no_conversations_found">Δεν βρέθηκαν αποθηκευμένες συνομιλίες</string>
<string name="start_conversation">Έναρξη συνομιλίας</string> <string name="start_conversation">Έναρξη συνομιλίας</string>
<string name="reply">Reply</string> <string name="reply">Απάντηση</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">Νέα συνομιλία</string> <string name="new_conversation">Νέα συνομιλία</string>
@@ -17,8 +18,8 @@
<!-- Notifications --> <!-- Notifications -->
<string name="channel_received_sms">Ελήφθη SMS</string> <string name="channel_received_sms">Ελήφθη SMS</string>
<string name="new_message">Νέο μήνυμα</string> <string name="new_message">Νέο μήνυμα</string>
<string name="mark_as_read">Σήμανση ως αναγνωσμένου</string> <string name="mark_as_read">Σήμανση ως αναγνωσμένο</string>
<string name="mark_as_unread">Mark as Unread</string> <string name="mark_as_unread">Σήμανση ως μη αναγνωσμένο</string>
<!-- Confirmation dialog --> <!-- Confirmation dialog -->
<string name="delete_whole_conversation_confirmation">Είστε βέβαιοι ότι θέλετε να διαγράψετε όλα τα μηνύματα αυτής της συνομιλίας;</string> <string name="delete_whole_conversation_confirmation">Είστε βέβαιοι ότι θέλετε να διαγράψετε όλα τα μηνύματα αυτής της συνομιλίας;</string>

View File

@@ -7,7 +7,8 @@
<string name="attachment">Archivo adjunto</string> <string name="attachment">Archivo adjunto</string>
<string name="no_conversations_found">No se han encontrado conversaciones</string> <string name="no_conversations_found">No se han encontrado conversaciones</string>
<string name="start_conversation">Inicia una conversación</string> <string name="start_conversation">Inicia una conversación</string>
<string name="reply">Reply</string> <string name="reply">Responder</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">Nueva conversación</string> <string name="new_conversation">Nueva conversación</string>
@@ -18,7 +19,7 @@
<string name="channel_received_sms">Mensaje recibico</string> <string name="channel_received_sms">Mensaje recibico</string>
<string name="new_message">Nuevo mensaje</string> <string name="new_message">Nuevo mensaje</string>
<string name="mark_as_read">Marcar como leído</string> <string name="mark_as_read">Marcar como leído</string>
<string name="mark_as_unread">Mark as Unread</string> <string name="mark_as_unread">Marcar como no leído</string>
<!-- Confirmation dialog --> <!-- Confirmation dialog -->
<string name="delete_whole_conversation_confirmation">¿Estás seguro que quieres eliminar todos los mensajes en esta conversación?</string> <string name="delete_whole_conversation_confirmation">¿Estás seguro que quieres eliminar todos los mensajes en esta conversación?</string>
@@ -37,7 +38,7 @@
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">¿Por qué la aplicación requiere acceso a internet?</string> <string name="faq_1_title">¿Por qué la aplicación requiere acceso a internet?</string>
<string name="faq_1_text">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. <string name="faq_1_text">Tristemente es necesitado para enviar archivos adjuntos MMS. El no poder enviar MMS sería una desventaja realmente enorme comparada con otras aplicaciones, así que decidimos tomar este camino.
Sin embargo, como siempre, no hay anuncios, rastreo o análisis, por lo que el internet solo es usado para enviar MMS.</string> Sin embargo, como siempre, no hay anuncios, rastreo o análisis, por lo que el internet solo es usado para enviar MMS.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->

View File

@@ -0,0 +1,74 @@
<resources>
<string name="app_name">Simple SMS Messenger</string>
<string name="app_launcher_name">SMS Messenger</string>
<string name="type_a_message">Écrivez un message...</string>
<string name="message_not_sent">Le message n\'a pas été envoyé.</string>
<string name="add_person">Ajouter une personne</string>
<string name="attachment">Pièce jointe</string>
<string name="no_conversations_found">Aucune conversation enregistrée n\'a été trouvée</string>
<string name="start_conversation">Commencer une conversation</string>
<string name="reply">Répondre</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation -->
<string name="new_conversation">Nouvelle conversation</string>
<string name="add_contact_or_number">Ajouter un contact ou un numéro...</string>
<string name="suggestions">Suggestions</string>
<!-- Notifications -->
<string name="channel_received_sms">SMS reçu</string>
<string name="new_message">Nouveau message</string>
<string name="mark_as_read">Marquer comme lu</string>
<string name="mark_as_unread">Marquer comme non lu</string>
<!-- Confirmation dialog -->
<string name="delete_whole_conversation_confirmation">Êtes-vous sûr de vouloir supprimer tous les messages de cette conversation ?</string>
<!-- Are you sure you want to delete 5 conversations? -->
<plurals name="delete_conversations">
<item quantity="one">%d conversation</item>
<item quantity="other">%d conversations</item>
</plurals>
<!-- Are you sure you want to delete 5 messages? -->
<plurals name="delete_messages">
<item quantity="one">%d message</item>
<item quantity="other">%d messages</item>
</plurals>
<!-- FAQ -->
<string name="faq_1_title">Pourquoi cette application a besoin d\'un accès à internet ?</string>
<string name="faq_1_text">Malheureusement, cela est nécessaire pour envoyer des pièces jointes dans les MMS. Ne pas pouvoir envoyer de MMS serait un énorme désavantage comparé à d\'autres applications, nous avons donc décidé de faire ainsi.
Cependant, comme toujours, il n\'y a aucune publicité, traqueur ou analyseur de quelque sorte, internet n\'est utilisé que pour envoyer des MMS.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple SMS Messenger - Gérez vos messages aisément</string>
<!-- Short description has to have max 80 characters -->
<string name="app_short_description">Une façon simple et rapide de gérer ses SMS et MMS sans publicités.</string>
<string name="app_long_description">
Une excellente façon de rester en contact avec vos proches, en envoyant à la fois des SMS et des MMS. L\'application gère parfaitement les messages de groupe, tout comme le blocage des numéros sur Android 7+.
Elle offre un choix large de format de date, pour être confortable à utiliser. Vous pouvez choisir entre un format 12 et 24 heures.
La taille de l\'application est très légère comparée à la concurrence, ce qui la rend rapide à télécharger.
Avec un Material Design et un thème sombre par défaut, elle offre une excellente expérience utilisateur pour une utilisation facile. L\'absence d\'accès à internet vous donne plus de confidentialité, de sécurité et de stabilité que les autres applications.
Ne contient aucune publicité ou autorisation inutile. Elle est entièrement open source, avec des couleurs personnalisables.
<b>Découvrez la suite complète des applications Simple Mobile Tools ici :</b>
https://www.simplemobiletools.com
<b>Facebook:</b>
https://www.facebook.com/simplemobiletools
<b>Reddit:</b>
https://www.reddit.com/r/SimpleMobileTools
</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@@ -0,0 +1,72 @@
<resources>
<string name="app_name">Simple SMS Messenger</string>
<string name="app_launcher_name">Mensaxe SMS</string>
<string name="type_a_message">Escribir unha mensaxe…</string>
<string name="message_not_sent">Mensaxe non enviada</string>
<string name="add_person">Engadir persoa</string>
<string name="attachment">Anexo</string>
<string name="no_conversations_found">Non se atoparon conversas</string>
<string name="start_conversation">Iniciar unha conversa</string>
<string name="reply">Responder</string>
<!-- New conversation -->
<string name="new_conversation">Nova conversa</string>
<string name="add_contact_or_number">Engadir contacto ou número…</string>
<string name="suggestions">Suxerencias</string>
<!-- Notifications -->
<string name="channel_received_sms">SMS recibida</string>
<string name="new_message">Nova mensaxe</string>
<string name="mark_as_read">Marcar como lida</string>
<string name="mark_as_unread">Marcar como non lida</string>
<!-- Confirmation dialog -->
<string name="delete_whole_conversation_confirmation">Ten a certeza de que desexa eliminar todas as mensaxes desta conversa?</string>
<!-- Are you sure you want to delete 5 conversations? -->
<plurals name="delete_conversations">
<item quantity="one">%d conversa</item>
<item quantity="other">%d conversas</item>
</plurals>
<!-- Are you sure you want to delete 5 messages? -->
<plurals name="delete_messages">
<item quantity="one">%d mensaxe</item>
<item quantity="other">%d mensaxes</item>
</plurals>
<!-- FAQ -->
<string name="faq_1_title">Por que o aplicativo necesita acceder a Internet?</string>
<string name="faq_1_text">Infelizmente é a única forma para poder enviar anexos MMS. A incapacidade de non conseguir enviar MMS sería unha enorme desvantaxe comparativamente a outros aplicativos e, por iso, tomamos esta decisión. Pero, como habitualmente, o aplicativo non ten anuncios, non rastrea os utilizadores nin recolle datos persoais. Este permiso só é necesario para enviar as MMS.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple SMS Messenger - Xestor de mensaxes</string>
<!-- Short description has to have max 80 characters -->
<string name="app_short_description">Aplictivo simple para xestionar SMS e MMS sen anuncios.</string>
<string name="app_long_description">
Unha excelente forma para manter o contacto cos seus amigos e familiares. Tamén pode ser usada para mensaxes de grupo e posibilita o bloqueo de números de teléfono nas versións superiores a Android 7.
Permite a utilización de varios formatos de data e das horas.
É un aplicativo pequeno, que fai que a súa descarga sexa moi rápida.
Fai que estea dispoñible un deseño atractivo e un tema escuro por defecto. Como non necesita o acceso a Internet, tes máis privacidade, seguranza e estabilidade.
Non ten anuncios nin permisos innecesarios. É de código aberto e permite a personalización das cores.
<b>Visita a páxina a través desta ligazón:</b>
https://www.simplemobiletools.com
<b>Facebook:</b>
https://www.facebook.com/simplemobiletools
<b>Reddit:</b>
https://www.reddit.com/r/SimpleMobileTools
</string>
<!--
Non atopaches algunhas cadeas? Hai máis en
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@@ -8,6 +8,7 @@
<string name="no_conversations_found">Nebuvo rasta išsaugotų pokalbių</string> <string name="no_conversations_found">Nebuvo rasta išsaugotų pokalbių</string>
<string name="start_conversation">Pradėtipokalbį</string> <string name="start_conversation">Pradėtipokalbį</string>
<string name="reply">Reply</string> <string name="reply">Reply</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">Naujas pokalbis</string> <string name="new_conversation">Naujas pokalbis</string>

View File

@@ -0,0 +1,74 @@
<resources>
<string name="app_name">Simple SMS Messenger</string>
<string name="app_launcher_name">SMS മെസഞ്ചർ</string>
<string name="type_a_message">മെസ്സേജ് ടൈപ്പ് ചെയ്യുക</string>
<string name="message_not_sent">മെസ്സേജ് അയച്ചിട്ടില്ല.</string>
<string name="add_person">വ്യക്തിയെ ചേർക്കുക</string>
<string name="attachment">അറ്റാച്ചുമെന്റ്</string>
<string name="no_conversations_found">സ്റ്റോർ ചെയ്ത സംഭാഷണങ്ങളൊന്നും കണ്ടെത്തിയില്ല</string>
<string name="start_conversation">ഒരു സംഭാഷണം ആരംഭിക്കുക</string>
<string name="reply">ഒരു സംഭാഷണം ആരംഭിക്കുക</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation -->
<string name="new_conversation">പുതിയ സംഭാഷണം</string>
<string name="add_contact_or_number">കോൺടാക്റ്റ് അല്ലെങ്കിൽ നമ്പർ ചേർക്കുക…</string>
<string name="suggestions">നിർദ്ദേശങ്ങൾ</string>
<!-- Notifications -->
<string name="channel_received_sms">SMS ലഭിച്ചു</string>
<string name="new_message">പുതിയ മെസ്സേജ്</string>
<string name="mark_as_read">വായിച്ചതായി അടയാളപ്പെടുത്തുക</string>
<string name="mark_as_unread">വായിച്ചിട്ടില്ലെന്ന് അടയാളപ്പെടുത്തുക</string>
<!-- Confirmation dialog -->
<string name="delete_whole_conversation_confirmation">ഈ സംഭാഷണത്തിന്റെ എല്ലാ സന്ദേശങ്ങൾ ഇല്ലാതാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ?</string>
<!-- Are you sure you want to delete 5 conversations? -->
<plurals name="delete_conversations">
<item quantity="one">%d conversation</item>
<item quantity="other">%d conversations</item>
</plurals>
<!-- Are you sure you want to delete 5 messages? -->
<plurals name="delete_messages">
<item quantity="one">%d message</item>
<item quantity="other">%d messages</item>
</plurals>
<!-- FAQ -->
<string name="faq_1_title">അപ്ലിക്കേഷന് ഇന്റർനെറ്റിലേക്ക് ആവശ്യമായി വരുന്നത് എന്തുകൊണ്ട്?</string>
<string name="faq_1_text">നിർഭാഗ്യവശാൽ, MMS അറ്റാച്ചുമെന്റുകൾ അയക്കുന്നതിനു ഇത് ആവശ്യമാണ്. മറ്റ് ആപ്ലിക്കേഷനുകളുമായി താരതമ്യപ്പെടുത്തുമ്പോൾ MMS അയയ്ക്കാൻ കഴിയുന്നില്ല എന്നത് ഒരു വലിയ പോരായ്മയാണ്, അതിനാൽ ഞങ്ങൾ ഈ റൂട്ടിൽ പോകാൻ തീരുമാനിച്ചു.
എന്നിരുന്നാലും, സാധാരണപോലെ, പരസ്യങ്ങളോ ട്രാക്കിംഗ് അല്ലെങ്കിൽ അനലിറ്റിക്സുകളോ ഇല്ല. ഇന്റർനെറ്റ് MMS അയയ്ക്കുന്നതിന് മാത്രമാണ് ഉപയോഗിക്കുന്നത്.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple SMS Messenger - മെസ്സേജുകൾ കൈകാര്യം ചെയ്യുക</string>
<!-- Short description has to have max 80 characters -->
<string name="app_short_description">SMS, MMS സന്ദേശങ്ങൾ കൈകാര്യം ചെയ്യുന്നതിനുള്ള ഏറ്റവും എളുപ്പ മാർഗം.</string>
<string name="app_long_description">
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.
<b>Check out the full suite of Simple Tools here:</b>
https://www.simplemobiletools.com
<b>Facebook:</b>
https://www.facebook.com/simplemobiletools
<b>Reddit:</b>
https://www.reddit.com/r/SimpleMobileTools
</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@@ -8,6 +8,7 @@
<string name="no_conversations_found">Geen opgeslagen berichten gevonden</string> <string name="no_conversations_found">Geen opgeslagen berichten gevonden</string>
<string name="start_conversation">Een gesprek starten</string> <string name="start_conversation">Een gesprek starten</string>
<string name="reply">Beantwoorden</string> <string name="reply">Beantwoorden</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">Nieuw gesprek</string> <string name="new_conversation">Nieuw gesprek</string>

View File

@@ -1,13 +1,14 @@
<resources> <resources>
<string name="app_name">Simple SMS Messenger</string> <string name="app_name">Simple SMS Messenger</string>
<string name="app_launcher_name">SMS Messenger</string> <string name="app_launcher_name">SMS Messenger</string>
<string name="type_a_message">Escreva uma mensagem…</string> <string name="type_a_message">Escrever uma mensagem…</string>
<string name="message_not_sent">Mensagem não enviada.</string> <string name="message_not_sent">Mensagem não enviada</string>
<string name="add_person">Adicionar pessoa</string> <string name="add_person">Adicionar pessoa</string>
<string name="attachment">Anexo</string> <string name="attachment">Anexo</string>
<string name="no_conversations_found">Não foram encontradas conversas</string> <string name="no_conversations_found">Não foram encontradas conversas</string>
<string name="start_conversation">Iniciar uma conversa</string> <string name="start_conversation">Iniciar uma conversa</string>
<string name="reply">Reply</string> <string name="reply">Responder</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">Nova conversa</string> <string name="new_conversation">Nova conversa</string>
@@ -18,7 +19,7 @@
<string name="channel_received_sms">SMS recebida</string> <string name="channel_received_sms">SMS recebida</string>
<string name="new_message">Nova mensagem</string> <string name="new_message">Nova mensagem</string>
<string name="mark_as_read">Marcar como lida</string> <string name="mark_as_read">Marcar como lida</string>
<string name="mark_as_unread">Mark as Unread</string> <string name="mark_as_unread">Marcar como não lida</string>
<!-- Confirmation dialog --> <!-- Confirmation dialog -->
<string name="delete_whole_conversation_confirmation">Tem a certeza de que deseja eliminar todas as mensagens desta conversa?</string> <string name="delete_whole_conversation_confirmation">Tem a certeza de que deseja eliminar todas as mensagens desta conversa?</string>

View File

@@ -8,6 +8,7 @@
<string name="no_conversations_found">Нет сохранённых переписок</string> <string name="no_conversations_found">Нет сохранённых переписок</string>
<string name="start_conversation">Начать переписку</string> <string name="start_conversation">Начать переписку</string>
<string name="reply">Ответ</string> <string name="reply">Ответ</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">Новая переписка</string> <string name="new_conversation">Новая переписка</string>

View File

@@ -8,6 +8,7 @@
<string name="no_conversations_found">Nenašli sa žiadne uložené konverzácie</string> <string name="no_conversations_found">Nenašli sa žiadne uložené konverzácie</string>
<string name="start_conversation">Začať konverzáciu</string> <string name="start_conversation">Začať konverzáciu</string>
<string name="reply">Odpovedať</string> <string name="reply">Odpovedať</string>
<string name="show_character_counter">Zobraziť počítadlo znakov pri písaní správ</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">Nová konverzácia</string> <string name="new_conversation">Nová konverzácia</string>

View File

@@ -0,0 +1,74 @@
<resources>
<string name="app_name">简易短信</string>
<string name="app_launcher_name">短信</string>
<string name="type_a_message">输入一个消息…</string>
<string name="message_not_sent">消息尚未发送。</string>
<string name="add_person">添加人</string>
<string name="attachment">附件</string>
<string name="no_conversations_found">未找到保存的对话</string>
<string name="start_conversation">开始一个对话</string>
<string name="reply">回复</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation -->
<string name="new_conversation">新的对话</string>
<string name="add_contact_or_number">添加联系人或者号码…</string>
<string name="suggestions">建议</string>
<!-- Notifications -->
<string name="channel_received_sms">接收到的短信</string>
<string name="new_message">新消息</string>
<string name="mark_as_read">标记为已读</string>
<string name="mark_as_unread">标记为未读</string>
<!-- Confirmation dialog -->
<string name="delete_whole_conversation_confirmation">您确定您想要删除这个对话的所有消息?</string>
<!-- Are you sure you want to delete 5 conversations? -->
<plurals name="delete_conversations">
<item quantity="one">%d 个对话</item>
<item quantity="other">%d 个对话</item>
</plurals>
<!-- Are you sure you want to delete 5 messages? -->
<plurals name="delete_messages">
<item quantity="one">%d 个消息</item>
<item quantity="other">%d 个消息</item>
</plurals>
<!-- FAQ -->
<string name="faq_1_title">为什么该应用需要访问互联网?</string>
<string name="faq_1_text">很遗憾这对于发送彩信附件是必须的。如果不能发送彩信的话这相比其他应用会是一个巨大的劣势,所以我们决定这么采取现在的方式。
但是和其他应用一样,不包含广告、追踪或者任意的分析工具, 互联网访问仅用于发送彩信。</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">简易短信 - 轻松管理消息</string>
<!-- Short description has to have max 80 characters -->
<string name="app_short_description">无广告的管理短信和彩信的轻松和简便方式。</string>
<string name="app_long_description">
发送短信和彩信是一个和您亲友保持联系的绝佳方式。这个应用也可以可以正确处理群消息,在 Android 7 以上版本亦可阻止号码。
它提供多个日期格式以便选择,符合您使用习惯。您也可以在 12 和 24 时制格式之间选择。
它相对于其他竞品有着极小的大小,使得其下载非常快。
它遵循质感设计且默认应用黑暗模式,提供便于使用的极佳的用户体验。
不包含广告及非必要的权限,而且完全开放源代码,并提供自定义颜色。
<b>于此查看简易工具系列全套:</b>
https://www.simplemobiletools.com
<b>Facebook:</b>
https://www.facebook.com/simplemobiletools
<b>Reddit:</b>
https://www.reddit.com/r/SimpleMobileTools
</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@@ -0,0 +1,5 @@
<resources>
<string name="package_name">com.simplemobiletools.smsmessenger</string>
</resources>

View File

@@ -8,6 +8,7 @@
<string name="no_conversations_found">No stored conversations have been found</string> <string name="no_conversations_found">No stored conversations have been found</string>
<string name="start_conversation">Start a conversation</string> <string name="start_conversation">Start a conversation</string>
<string name="reply">Reply</string> <string name="reply">Reply</string>
<string name="show_character_counter">Show a character counter at writing messages</string>
<!-- New conversation --> <!-- New conversation -->
<string name="new_conversation">New conversation</string> <string name="new_conversation">New conversation</string>

View File

@@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.72' ext.kotlin_version = '1.4.10'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.6.3' classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

View File

@@ -0,0 +1,3 @@
* Show unread message badges on some devices that support it
* Added some improvements related to handling private contacts
* Other stability, UI and translation improvements

View File

@@ -0,0 +1,2 @@
* Show a tick at successfully sent messages
* Properly show the sender name at incoming messages, if sent by a private contact

View File

@@ -0,0 +1 @@
* Fix incoming message notifications sometimes not being shown

View File

@@ -0,0 +1 @@
* Fixing a crash at receiving MMS

View File

@@ -0,0 +1 @@
* Fixing a crash at receiving MMS

View File

@@ -0,0 +1 @@
* Fixed some smaller glitches + translation improvements

View File

@@ -0,0 +1,4 @@
* Allow dialing or copying selected conversation phone numbers
* Allow copying specific parts of messages into clipboard
* Adding an option to show character counter at outgoing messages
* Couple other UI, translation and stability improvements

View File

@@ -1,6 +1,6 @@
#Fri Apr 03 09:58:13 CEST 2020 #Tue Nov 03 16:00:32 CET 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip