23
CHANGELOG.md
|
@ -1,6 +1,29 @@
|
||||||
Changelog
|
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)*
|
Version 5.1.2 *(2020-05-13)*
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ Reddit:
|
||||||
https://www.reddit.com/r/SimpleMobileTools
|
https://www.reddit.com/r/SimpleMobileTools
|
||||||
|
|
||||||
<a href='https://play.google.com/store/apps/details?id=com.simplemobiletools.smsmessenger'><img src='https://simplemobiletools.com/assets/images/google-play.png' alt='Get it on Google Play' height=45/></a>
|
<a href='https://play.google.com/store/apps/details?id=com.simplemobiletools.smsmessenger'><img src='https://simplemobiletools.com/assets/images/google-play.png' alt='Get it on Google Play' height=45/></a>
|
||||||
|
<a href='https://f-droid.org/packages/com.simplemobiletools.smsmessenger'><img src='https://simplemobiletools.com/assets/images/f-droid.png' alt='Get it on F-Droid' height='45' /></a>
|
||||||
|
|
||||||
<div style="display:flex;">
|
<div style="display:flex;">
|
||||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_1.jpg" width="30%">
|
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_1.jpg" width="30%">
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
def keystorePropertiesFile = rootProject.file("keystore.properties")
|
def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||||
def keystoreProperties = new Properties()
|
def keystoreProperties = new Properties()
|
||||||
|
@ -16,8 +17,8 @@ android {
|
||||||
applicationId "com.simplemobiletools.smsmessenger"
|
applicationId "com.simplemobiletools.smsmessenger"
|
||||||
minSdkVersion 22
|
minSdkVersion 22
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 5
|
versionCode 9
|
||||||
versionName "5.1.2"
|
versionName "5.2.0"
|
||||||
setProperty("archivesBaseName", "sms-messenger")
|
setProperty("archivesBaseName", "sms-messenger")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +57,12 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.simplemobiletools:commons:5.27.29'
|
implementation 'com.simplemobiletools:commons:5.28.25'
|
||||||
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'
|
||||||
|
|
||||||
|
kapt "androidx.room:room-compiler:2.2.5"
|
||||||
|
implementation "androidx.room:room-runtime:2.2.5"
|
||||||
|
annotationProcessor "androidx.room:room-compiler:2.2.5"
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,12 +40,12 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.NewConversationActivity"
|
android:name=".activities.NewConversationActivity"
|
||||||
android:noHistory="true"
|
|
||||||
android:parentActivityName=".activities.MainActivity">
|
android:parentActivityName=".activities.MainActivity">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
<action android:name="android.intent.action.SENDTO" />
|
<action android:name="android.intent.action.SENDTO" />
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.simplemobiletools.smsmessenger.BuildConfig
|
||||||
import com.simplemobiletools.smsmessenger.R
|
import com.simplemobiletools.smsmessenger.R
|
||||||
import com.simplemobiletools.smsmessenger.adapters.ConversationsAdapter
|
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.getConversations
|
import com.simplemobiletools.smsmessenger.extensions.getConversations
|
||||||
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
|
||||||
|
@ -128,9 +129,9 @@ class MainActivity : SimpleActivity() {
|
||||||
handlePermission(PERMISSION_SEND_SMS) {
|
handlePermission(PERMISSION_SEND_SMS) {
|
||||||
if (it) {
|
if (it) {
|
||||||
handlePermission(PERMISSION_READ_CONTACTS) {
|
handlePermission(PERMISSION_READ_CONTACTS) {
|
||||||
|
initMessenger()
|
||||||
bus = EventBus.getDefault()
|
bus = EventBus.getDefault()
|
||||||
bus!!.register(this)
|
bus!!.register(this)
|
||||||
initMessenger()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
finish()
|
finish()
|
||||||
|
@ -144,26 +145,7 @@ class MainActivity : SimpleActivity() {
|
||||||
|
|
||||||
private fun initMessenger() {
|
private fun initMessenger() {
|
||||||
storeStateVariables()
|
storeStateVariables()
|
||||||
ensureBackgroundThread {
|
getCachedConversations()
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
no_conversations_placeholder_2.setOnClickListener {
|
no_conversations_placeholder_2.setOnClickListener {
|
||||||
launchNewConversation()
|
launchNewConversation()
|
||||||
|
@ -174,6 +156,80 @@ class MainActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getCachedConversations() {
|
||||||
|
ensureBackgroundThread {
|
||||||
|
val conversations = conversationsDB.getAll().sortedByDescending { it.date }.toMutableList() as ArrayList<Conversation>
|
||||||
|
runOnUiThread {
|
||||||
|
setupConversations(conversations)
|
||||||
|
getNewConversations(conversations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNewConversations(cachedConversations: ArrayList<Conversation>) {
|
||||||
|
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<Conversation>) {
|
||||||
|
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() {
|
private fun launchNewConversation() {
|
||||||
Intent(this, NewConversationActivity::class.java).apply {
|
Intent(this, NewConversationActivity::class.java).apply {
|
||||||
startActivity(this)
|
startActivity(this)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.view.Menu
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
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.PERMISSION_READ_CONTACTS
|
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
|
||||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
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.getSuggestedContacts
|
||||||
import com.simplemobiletools.smsmessenger.extensions.getThreadId
|
import com.simplemobiletools.smsmessenger.extensions.getThreadId
|
||||||
import com.simplemobiletools.smsmessenger.helpers.*
|
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 kotlinx.android.synthetic.main.item_suggested_contact.view.*
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -25,10 +26,11 @@ import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class NewConversationActivity : SimpleActivity() {
|
class NewConversationActivity : SimpleActivity() {
|
||||||
private var allContacts = ArrayList<SimpleContact>()
|
private var allContacts = ArrayList<SimpleContact>()
|
||||||
|
private var privateContacts = ArrayList<SimpleContact>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_conversation)
|
setContentView(R.layout.activity_new_conversation)
|
||||||
title = getString(R.string.new_conversation)
|
title = getString(R.string.new_conversation)
|
||||||
updateTextColors(new_conversation_holder)
|
updateTextColors(new_conversation_holder)
|
||||||
|
|
||||||
|
@ -93,9 +95,10 @@ class NewConversationActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isThirdPartyIntent(): Boolean {
|
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()
|
val number = intent.dataString!!.removePrefix("sms:").removePrefix("smsto:").removePrefix("mms").removePrefix("mmsto:").trim()
|
||||||
launchThreadActivity(URLDecoder.decode(number), "")
|
launchThreadActivity(URLDecoder.decode(number), "")
|
||||||
|
finish()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -103,9 +106,14 @@ class NewConversationActivity : SimpleActivity() {
|
||||||
|
|
||||||
private fun fetchContacts() {
|
private fun fetchContacts() {
|
||||||
fillSuggestedContacts {
|
fillSuggestedContacts {
|
||||||
SimpleContactsHelper(this).getAvailableContacts {
|
SimpleContactsHelper(this).getAvailableContacts(false) {
|
||||||
allContacts = it
|
allContacts = it
|
||||||
|
|
||||||
|
if (privateContacts.isNotEmpty()) {
|
||||||
|
allContacts.addAll(privateContacts)
|
||||||
|
allContacts.sort()
|
||||||
|
}
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
setupAdapter(allContacts)
|
setupAdapter(allContacts)
|
||||||
}
|
}
|
||||||
|
@ -135,8 +143,10 @@ class NewConversationActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillSuggestedContacts(callback: () -> Unit) {
|
private fun fillSuggestedContacts(callback: () -> Unit) {
|
||||||
|
val privateCursor = getMyContactsContentProviderCursorLoader().loadInBackground()
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
val suggestions = getSuggestedContacts()
|
privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||||
|
val suggestions = getSuggestedContacts(privateContacts)
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
suggestions_holder.removeAllViews()
|
suggestions_holder.removeAllViews()
|
||||||
if (suggestions.isEmpty()) {
|
if (suggestions.isEmpty()) {
|
||||||
|
|
|
@ -30,10 +30,7 @@ import com.klinker.android.send_message.Settings
|
||||||
import com.klinker.android.send_message.Transaction
|
import com.klinker.android.send_message.Transaction
|
||||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||||
import com.simplemobiletools.commons.extensions.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
import com.simplemobiletools.commons.helpers.PERMISSION_READ_PHONE_STATE
|
import com.simplemobiletools.commons.helpers.*
|
||||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
|
||||||
import com.simplemobiletools.commons.helpers.isNougatPlus
|
|
||||||
import com.simplemobiletools.commons.models.SimpleContact
|
import com.simplemobiletools.commons.models.SimpleContact
|
||||||
import com.simplemobiletools.smsmessenger.R
|
import com.simplemobiletools.smsmessenger.R
|
||||||
import com.simplemobiletools.smsmessenger.adapters.AutoCompleteTextViewAdapter
|
import com.simplemobiletools.smsmessenger.adapters.AutoCompleteTextViewAdapter
|
||||||
|
@ -57,6 +54,7 @@ class ThreadActivity : SimpleActivity() {
|
||||||
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>()
|
||||||
|
private var privateContacts = ArrayList<SimpleContact>()
|
||||||
private var messages = ArrayList<Message>()
|
private var messages = ArrayList<Message>()
|
||||||
private val availableSIMCards = ArrayList<SIMCard>()
|
private val availableSIMCards = ArrayList<SIMCard>()
|
||||||
private var attachmentUris = LinkedHashSet<Uri>()
|
private var attachmentUris = LinkedHashSet<Uri>()
|
||||||
|
@ -89,6 +87,7 @@ class ThreadActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupThread() {
|
private fun setupThread() {
|
||||||
|
val privateCursor = getMyContactsContentProviderCursorLoader().loadInBackground()
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
messages = getMessages(threadId)
|
messages = getMessages(threadId)
|
||||||
participants = if (messages.isEmpty()) {
|
participants = if (messages.isEmpty()) {
|
||||||
|
@ -97,6 +96,25 @@ class ThreadActivity : SimpleActivity() {
|
||||||
messages.first().participants
|
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<String, String>()
|
||||||
|
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()) {
|
if (participants.isEmpty()) {
|
||||||
val name = intent.getStringExtra(THREAD_TITLE) ?: ""
|
val name = intent.getStringExtra(THREAD_TITLE) ?: ""
|
||||||
val number = intent.getStringExtra(THREAD_NUMBER)
|
val number = intent.getStringExtra(THREAD_NUMBER)
|
||||||
|
@ -202,9 +220,10 @@ class ThreadActivity : SimpleActivity() {
|
||||||
thread_messages_list.adapter = adapter
|
thread_messages_list.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleContactsHelper(this).getAvailableContacts {
|
SimpleContactsHelper(this).getAvailableContacts(false) { contacts ->
|
||||||
|
contacts.addAll(privateContacts)
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
val adapter = AutoCompleteTextViewAdapter(this, it)
|
val adapter = AutoCompleteTextViewAdapter(this, contacts)
|
||||||
add_contact_or_number.setAdapter(adapter)
|
add_contact_or_number.setAdapter(adapter)
|
||||||
add_contact_or_number.imeOptions = EditorInfo.IME_ACTION_NEXT
|
add_contact_or_number.imeOptions = EditorInfo.IME_ACTION_NEXT
|
||||||
add_contact_or_number.setOnItemClickListener { _, _, position, _ ->
|
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 number = add_contact_or_number.value
|
||||||
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", number)
|
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", number)
|
||||||
addSelectedContact(contact)
|
addSelectedContact(contact)
|
||||||
|
@ -307,10 +326,9 @@ class ThreadActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun blockNumber() {
|
private fun blockNumber() {
|
||||||
val baseString = R.string.block_confirmation
|
val numbers = participants.map { it.phoneNumber }
|
||||||
val numbers = participants.map { it.phoneNumber }.toTypedArray()
|
|
||||||
val numbersString = TextUtils.join(", ", numbers)
|
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) {
|
ConfirmationDialog(this, question) {
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
|
@ -325,11 +343,15 @@ class ThreadActivity : SimpleActivity() {
|
||||||
|
|
||||||
private fun askConfirmDelete() {
|
private fun askConfirmDelete() {
|
||||||
ConfirmationDialog(this, getString(R.string.delete_whole_conversation_confirmation)) {
|
ConfirmationDialog(this, getString(R.string.delete_whole_conversation_confirmation)) {
|
||||||
|
ensureBackgroundThread {
|
||||||
deleteConversation(threadId)
|
deleteConversation(threadId)
|
||||||
|
runOnUiThread {
|
||||||
refreshMessages()
|
refreshMessages()
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun managePeople() {
|
private fun managePeople() {
|
||||||
if (thread_add_contacts.isVisible()) {
|
if (thread_add_contacts.isVisible()) {
|
||||||
|
@ -399,6 +421,7 @@ class ThreadActivity : SimpleActivity() {
|
||||||
if (!it.read) {
|
if (!it.read) {
|
||||||
hadUnreadItems = true
|
hadUnreadItems = true
|
||||||
markMessageRead(it.id, it.isMMS)
|
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<Drawable>?, isFirstResource: Boolean): Boolean {
|
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
|
||||||
attachmentView.thread_attachment_preview.beGone()
|
attachmentView.thread_attachment_preview.beGone()
|
||||||
attachmentView.thread_remove_attachment.beGone()
|
attachmentView.thread_remove_attachment.beGone()
|
||||||
showErrorToast(e?.localizedMessage ?: "")
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,9 +519,13 @@ class ThreadActivity : SimpleActivity() {
|
||||||
|
|
||||||
if (attachmentUris.isNotEmpty()) {
|
if (attachmentUris.isNotEmpty()) {
|
||||||
for (uri in attachmentUris) {
|
for (uri in attachmentUris) {
|
||||||
|
try {
|
||||||
val byteArray = contentResolver.openInputStream(uri)?.readBytes() ?: continue
|
val byteArray = contentResolver.openInputStream(uri)?.readBytes() ?: continue
|
||||||
val mimeType = contentResolver.getType(uri) ?: continue
|
val mimeType = contentResolver.getType(uri) ?: continue
|
||||||
message.addMedia(byteArray, mimeType)
|
message.addMedia(byteArray, mimeType)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showErrorToast(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import com.simplemobiletools.smsmessenger.R
|
||||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||||
|
|
||||||
class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: ArrayList<SimpleContact>) : ArrayAdapter<SimpleContact>(activity, 0, contacts) {
|
class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: ArrayList<SimpleContact>) : ArrayAdapter<SimpleContact>(activity, 0, contacts) {
|
||||||
|
|
||||||
var resultList = ArrayList<SimpleContact>()
|
var resultList = ArrayList<SimpleContact>()
|
||||||
|
|
||||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
|
@ -25,6 +24,12 @@ class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: Ar
|
||||||
|
|
||||||
listItem!!.apply {
|
listItem!!.apply {
|
||||||
tag = contact.name.isNotEmpty()
|
tag = contact.name.isNotEmpty()
|
||||||
|
// clickable and focusable properties seem to break Autocomplete clicking, so remove them
|
||||||
|
findViewById<View>(R.id.item_contact_frame).apply {
|
||||||
|
isClickable = false
|
||||||
|
isFocusable = false
|
||||||
|
}
|
||||||
|
|
||||||
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.phoneNumber
|
||||||
|
|
||||||
|
|
|
@ -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.text.TextUtils
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -9,11 +10,13 @@ import android.widget.TextView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
|
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
|
||||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||||
|
import com.simplemobiletools.commons.extensions.addBlockedNumber
|
||||||
import com.simplemobiletools.commons.extensions.formatDateOrTime
|
import com.simplemobiletools.commons.extensions.formatDateOrTime
|
||||||
import com.simplemobiletools.commons.extensions.toast
|
import com.simplemobiletools.commons.extensions.toast
|
||||||
import com.simplemobiletools.commons.helpers.KEY_PHONE
|
import com.simplemobiletools.commons.helpers.KEY_PHONE
|
||||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
|
import com.simplemobiletools.commons.helpers.isNougatPlus
|
||||||
import com.simplemobiletools.commons.views.FastScroller
|
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
|
||||||
|
@ -35,6 +38,7 @@ 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_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) {
|
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_select_all -> selectAll()
|
R.id.cab_select_all -> selectAll()
|
||||||
R.id.cab_delete -> askConfirmDelete()
|
R.id.cab_delete -> askConfirmDelete()
|
||||||
}
|
}
|
||||||
|
@ -54,9 +59,9 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
||||||
|
|
||||||
override fun getIsItemSelectable(position: Int) = true
|
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() {}
|
override fun onActionModeCreated() {}
|
||||||
|
|
||||||
|
@ -74,6 +79,37 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
||||||
|
|
||||||
override fun getItemCount() = conversations.size
|
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() {
|
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)
|
||||||
|
@ -93,10 +129,10 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val conversationsToRemove = conversations.filter { selectedKeys.contains(it.id) } as ArrayList<Conversation>
|
val conversationsToRemove = conversations.filter { selectedKeys.contains(it.thread_id) } as ArrayList<Conversation>
|
||||||
val positions = getSelectedItemPositions()
|
val positions = getSelectedItemPositions()
|
||||||
conversationsToRemove.forEach {
|
conversationsToRemove.forEach {
|
||||||
activity.deleteConversation(it.id)
|
activity.deleteConversation(it.thread_id)
|
||||||
}
|
}
|
||||||
conversations.removeAll(conversationsToRemove)
|
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<Conversation>
|
private fun getSelectedItems() = conversations.filter { selectedKeys.contains(it.thread_id) } as ArrayList<Conversation>
|
||||||
|
|
||||||
override fun onViewRecycled(holder: ViewHolder) {
|
override fun onViewRecycled(holder: ViewHolder) {
|
||||||
super.onViewRecycled(holder)
|
super.onViewRecycled(holder)
|
||||||
|
@ -137,9 +173,18 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateConversations(newConversations: ArrayList<Conversation>) {
|
||||||
|
val oldHashCode = conversations.hashCode()
|
||||||
|
val newHashCode = newConversations.hashCode()
|
||||||
|
if (newHashCode != oldHashCode) {
|
||||||
|
conversations = newConversations
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupView(view: View, conversation: Conversation) {
|
private fun setupView(view: View, conversation: Conversation) {
|
||||||
view.apply {
|
view.apply {
|
||||||
conversation_frame.isSelected = selectedKeys.contains(conversation.id)
|
conversation_frame.isSelected = selectedKeys.contains(conversation.thread_id)
|
||||||
|
|
||||||
conversation_address.text = conversation.title
|
conversation_address.text = conversation.title
|
||||||
conversation_body_short.text = conversation.snippet
|
conversation_body_short.text = conversation.snippet
|
||||||
|
|
|
@ -44,6 +44,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
||||||
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
||||||
|
|
||||||
private val roundedCornersRadius = resources.getDimension(R.dimen.normal_margin).toInt()
|
private val roundedCornersRadius = resources.getDimension(R.dimen.normal_margin).toInt()
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
private val hasMultipleSIMCards = SubscriptionManager.from(activity).activeSubscriptionInfoList?.size ?: 0 > 1
|
private val hasMultipleSIMCards = SubscriptionManager.from(activity).activeSubscriptionInfoList?.size ?: 0 > 1
|
||||||
|
|
||||||
|
|
|
@ -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!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,5 +2,8 @@ package com.simplemobiletools.smsmessenger.extensions
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import com.simplemobiletools.commons.models.SimpleContact
|
import com.simplemobiletools.commons.models.SimpleContact
|
||||||
|
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||||
|
|
||||||
fun ArrayList<SimpleContact>.getThreadTitle() = TextUtils.join(", ", map { it.name }.toTypedArray())
|
fun ArrayList<SimpleContact>.getThreadTitle() = TextUtils.join(", ", map { it.name }.toTypedArray())
|
||||||
|
|
||||||
|
fun ArrayList<Conversation>.getHashToCompare() = map { it.getStringToCompare() }.hashCode()
|
||||||
|
|
|
@ -22,7 +22,9 @@ import com.simplemobiletools.commons.helpers.*
|
||||||
import com.simplemobiletools.commons.models.SimpleContact
|
import com.simplemobiletools.commons.models.SimpleContact
|
||||||
import com.simplemobiletools.smsmessenger.R
|
import com.simplemobiletools.smsmessenger.R
|
||||||
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
|
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
|
||||||
|
import com.simplemobiletools.smsmessenger.databases.MessagesDatabase
|
||||||
import com.simplemobiletools.smsmessenger.helpers.*
|
import com.simplemobiletools.smsmessenger.helpers.*
|
||||||
|
import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
|
||||||
import com.simplemobiletools.smsmessenger.models.*
|
import com.simplemobiletools.smsmessenger.models.*
|
||||||
import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver
|
import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -30,6 +32,10 @@ import kotlin.collections.ArrayList
|
||||||
|
|
||||||
val Context.config: Config get() = Config.newInstance(applicationContext)
|
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<Message> {
|
fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
||||||
val uri = Sms.CONTENT_URI
|
val uri = Sms.CONTENT_URI
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
|
@ -48,6 +54,7 @@ fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
||||||
val sortOrder = "${Sms._ID} DESC LIMIT 100"
|
val sortOrder = "${Sms._ID} DESC LIMIT 100"
|
||||||
|
|
||||||
val blockStatus = HashMap<String, Boolean>()
|
val blockStatus = HashMap<String, Boolean>()
|
||||||
|
val blockedNumbers = getBlockedNumbers()
|
||||||
var messages = ArrayList<Message>()
|
var messages = ArrayList<Message>()
|
||||||
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
|
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
|
||||||
val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor
|
val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor
|
||||||
|
@ -55,7 +62,7 @@ fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
||||||
val isNumberBlocked = if (blockStatus.containsKey(senderNumber)) {
|
val isNumberBlocked = if (blockStatus.containsKey(senderNumber)) {
|
||||||
blockStatus[senderNumber]!!
|
blockStatus[senderNumber]!!
|
||||||
} else {
|
} else {
|
||||||
val isBlocked = isNumberBlocked(senderNumber)
|
val isBlocked = isNumberBlocked(senderNumber, blockedNumbers)
|
||||||
blockStatus[senderNumber] = isBlocked
|
blockStatus[senderNumber] = isBlocked
|
||||||
isBlocked
|
isBlocked
|
||||||
}
|
}
|
||||||
|
@ -148,7 +155,7 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<
|
||||||
messages.add(message)
|
messages.add(message)
|
||||||
|
|
||||||
participants.forEach {
|
participants.forEach {
|
||||||
contactsMap.put(it.rawId, it)
|
contactsMap[it.rawId] = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +180,7 @@ fun Context.getMMSSender(msgId: Int): String {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getConversations(): ArrayList<Conversation> {
|
fun Context.getConversations(threadId: Long? = null): ArrayList<Conversation> {
|
||||||
val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true")
|
val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true")
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
Threads._ID,
|
Threads._ID,
|
||||||
|
@ -183,10 +190,19 @@ fun Context.getConversations(): ArrayList<Conversation> {
|
||||||
Threads.RECIPIENT_IDS
|
Threads.RECIPIENT_IDS
|
||||||
)
|
)
|
||||||
|
|
||||||
val selection = "${Threads.MESSAGE_COUNT} > ?"
|
var selection = "${Threads.MESSAGE_COUNT} > ?"
|
||||||
val selectionArgs = arrayOf("0")
|
var selectionArgs = arrayOf("0")
|
||||||
|
if (threadId != null) {
|
||||||
|
selection += " AND ${Threads._ID} = ?"
|
||||||
|
selectionArgs = arrayOf("0", threadId.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
val sortOrder = "${Threads.DATE} DESC"
|
||||||
|
|
||||||
val conversations = ArrayList<Conversation>()
|
val conversations = ArrayList<Conversation>()
|
||||||
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)
|
val id = cursor.getIntValue(Threads._ID)
|
||||||
var snippet = cursor.getStringValue(Threads.SNIPPET) ?: ""
|
var snippet = cursor.getStringValue(Threads.SNIPPET) ?: ""
|
||||||
if (snippet.isEmpty()) {
|
if (snippet.isEmpty()) {
|
||||||
|
@ -198,22 +214,24 @@ fun Context.getConversations(): ArrayList<Conversation> {
|
||||||
date /= 1000
|
date /= 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
val read = cursor.getIntValue(Threads.READ) == 1
|
|
||||||
val rawIds = cursor.getStringValue(Threads.RECIPIENT_IDS)
|
val rawIds = cursor.getStringValue(Threads.RECIPIENT_IDS)
|
||||||
val recipientIds = rawIds.split(" ").filter { it.areDigitsOnly() }.map { it.toInt() }.toMutableList()
|
val recipientIds = rawIds.split(" ").filter { it.areDigitsOnly() }.map { it.toInt() }.toMutableList()
|
||||||
val phoneNumbers = getThreadPhoneNumbers(recipientIds)
|
val phoneNumbers = getThreadPhoneNumbers(recipientIds)
|
||||||
if (phoneNumbers.any { isNumberBlocked(it) }) {
|
if (phoneNumbers.any { isNumberBlocked(it, blockedNumbers) }) {
|
||||||
return@queryCursor
|
return@queryCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
val names = getThreadContactNames(phoneNumbers)
|
val names = getThreadContactNames(phoneNumbers)
|
||||||
val title = TextUtils.join(", ", names.toTypedArray())
|
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 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.add(conversation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conversations.sortByDescending { it.date }
|
||||||
return conversations
|
return conversations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +381,7 @@ fun Context.getPhoneNumberFromAddressId(canonicalAddressId: Int): String {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getSuggestedContacts(): ArrayList<SimpleContact> {
|
fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): ArrayList<SimpleContact> {
|
||||||
val contacts = ArrayList<SimpleContact>()
|
val contacts = ArrayList<SimpleContact>()
|
||||||
val uri = Sms.CONTENT_URI
|
val uri = Sms.CONTENT_URI
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
|
@ -373,16 +391,29 @@ fun Context.getSuggestedContacts(): ArrayList<SimpleContact> {
|
||||||
val selection = "1 == 1) GROUP BY (${Sms.ADDRESS}"
|
val selection = "1 == 1) GROUP BY (${Sms.ADDRESS}"
|
||||||
val selectionArgs = null
|
val selectionArgs = null
|
||||||
val sortOrder = "${Sms.DATE} DESC LIMIT 20"
|
val sortOrder = "${Sms.DATE} DESC LIMIT 20"
|
||||||
|
val blockedNumbers = getBlockedNumbers()
|
||||||
|
|
||||||
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
|
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
|
||||||
val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor
|
val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor
|
||||||
val namePhoto = getNameAndPhotoFromPhoneNumber(senderNumber)
|
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
|
return@queryCursor
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return@queryCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val senderName = namePhoto.name
|
|
||||||
val photoUri = namePhoto.photoUri ?: ""
|
|
||||||
val contact = SimpleContact(0, 0, senderName, photoUri, senderNumber)
|
val contact = SimpleContact(0, 0, senderName, photoUri, senderNumber)
|
||||||
if (!contacts.map { it.phoneNumber.trimToComparableNumber() }.contains(senderNumber.trimToComparableNumber())) {
|
if (!contacts.map { it.phoneNumber.trimToComparableNumber() }.contains(senderNumber.trimToComparableNumber())) {
|
||||||
contacts.add(contact)
|
contacts.add(contact)
|
||||||
|
@ -436,14 +467,16 @@ fun Context.insertNewSMS(address: String, subject: String, body: String, date: L
|
||||||
return newUri?.lastPathSegment?.toInt() ?: 0
|
return newUri?.lastPathSegment?.toInt() ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.deleteConversation(id: Int) {
|
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(id.toString())
|
val selectionArgs = arrayOf(threadId.toString())
|
||||||
contentResolver.delete(uri, selection, selectionArgs)
|
contentResolver.delete(uri, selection, selectionArgs)
|
||||||
|
|
||||||
uri = Mms.CONTENT_URI
|
uri = Mms.CONTENT_URI
|
||||||
contentResolver.delete(uri, selection, selectionArgs)
|
contentResolver.delete(uri, selection, selectionArgs)
|
||||||
|
|
||||||
|
conversationsDB.deleteThreadId(threadId.toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.deleteMessage(id: Int, isMMS: Boolean) {
|
fun Context.deleteMessage(id: Int, isMMS: Boolean) {
|
||||||
|
@ -482,16 +515,6 @@ fun Context.getThreadId(addresses: Set<String>): 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")
|
@SuppressLint("NewApi")
|
||||||
fun Context.showReceivedMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?, messageId: Int, isMMS: Boolean) {
|
fun Context.showReceivedMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?, messageId: Int, isMMS: Boolean) {
|
||||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
@ -527,6 +550,7 @@ fun Context.showReceivedMessageNotification(address: String, body: String, threa
|
||||||
action = MARK_AS_READ
|
action = MARK_AS_READ
|
||||||
putExtra(MESSAGE_ID, messageId)
|
putExtra(MESSAGE_ID, messageId)
|
||||||
putExtra(MESSAGE_IS_MMS, isMMS)
|
putExtra(MESSAGE_IS_MMS, isMMS)
|
||||||
|
putExtra(THREAD_ID, threadID)
|
||||||
}
|
}
|
||||||
val markAsReadPendingIntent = PendingIntent.getBroadcast(this, 0, markAsReadIntent, PendingIntent.FLAG_CANCEL_CURRENT)
|
val markAsReadPendingIntent = PendingIntent.getBroadcast(this, 0, markAsReadIntent, PendingIntent.FLAG_CANCEL_CURRENT)
|
||||||
|
|
||||||
|
|
|
@ -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<Conversation>
|
||||||
|
|
||||||
|
@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)
|
||||||
|
}
|
|
@ -1,5 +1,23 @@
|
||||||
package com.simplemobiletools.smsmessenger.models
|
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(
|
data class Conversation(
|
||||||
val id: Int, val snippet: String, val date: Int, val read: Boolean, val title: String, val photoUri: String,
|
@PrimaryKey(autoGenerate = true) var id: Long?,
|
||||||
val isGroupConversation: Boolean, val phoneNumber: String)
|
@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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,6 @@ import com.simplemobiletools.commons.models.SimpleContact
|
||||||
|
|
||||||
data class Message(
|
data class Message(
|
||||||
val id: Int, val body: String, val type: Int, val participants: ArrayList<SimpleContact>, val date: Int, val read: Boolean, val thread: Int,
|
val id: Int, val body: String, val type: Int, val participants: ArrayList<SimpleContact>, 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
|
fun isReceivedMessage() = type == Telephony.Sms.MESSAGE_TYPE_INBOX
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,27 @@ import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.simplemobiletools.commons.extensions.notificationManager
|
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.extensions.markMessageRead
|
||||||
import com.simplemobiletools.smsmessenger.helpers.MARK_AS_READ
|
import com.simplemobiletools.smsmessenger.helpers.MARK_AS_READ
|
||||||
import com.simplemobiletools.smsmessenger.helpers.MESSAGE_ID
|
import com.simplemobiletools.smsmessenger.helpers.MESSAGE_ID
|
||||||
import com.simplemobiletools.smsmessenger.helpers.MESSAGE_IS_MMS
|
import com.simplemobiletools.smsmessenger.helpers.MESSAGE_IS_MMS
|
||||||
|
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
|
||||||
|
|
||||||
class MarkAsReadReceiver : BroadcastReceiver() {
|
class MarkAsReadReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
MARK_AS_READ -> {
|
MARK_AS_READ -> {
|
||||||
val messageId = intent.getIntExtra(MESSAGE_ID, 0)
|
val messageId = intent.getIntExtra(MESSAGE_ID, 0)
|
||||||
|
context.notificationManager.cancel(messageId)
|
||||||
|
ensureBackgroundThread {
|
||||||
val isMMS = intent.getBooleanExtra(MESSAGE_IS_MMS, false)
|
val isMMS = intent.getBooleanExtra(MESSAGE_IS_MMS, false)
|
||||||
context.markMessageRead(messageId, isMMS)
|
context.markMessageRead(messageId, isMMS)
|
||||||
context.notificationManager.cancel(messageId)
|
|
||||||
|
val threadId = intent.getIntExtra(THREAD_ID, 0)
|
||||||
|
context.conversationsDB.markRead(threadId.toLong())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,12 @@ package com.simplemobiletools.smsmessenger.receivers
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
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.getConversations
|
||||||
import com.simplemobiletools.smsmessenger.extensions.getLatestMMS
|
import com.simplemobiletools.smsmessenger.extensions.getLatestMMS
|
||||||
import com.simplemobiletools.smsmessenger.extensions.isNumberBlocked
|
|
||||||
import com.simplemobiletools.smsmessenger.extensions.showReceivedMessageNotification
|
import com.simplemobiletools.smsmessenger.extensions.showReceivedMessageNotification
|
||||||
|
|
||||||
// more info at https://github.com/klinker41/android-smsmms
|
// 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)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,9 @@ import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.provider.Telephony
|
import android.provider.Telephony
|
||||||
import com.simplemobiletools.smsmessenger.extensions.getThreadId
|
import com.simplemobiletools.commons.extensions.isNumberBlocked
|
||||||
import com.simplemobiletools.smsmessenger.extensions.insertNewSMS
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
import com.simplemobiletools.smsmessenger.extensions.isNumberBlocked
|
import com.simplemobiletools.smsmessenger.extensions.*
|
||||||
import com.simplemobiletools.smsmessenger.extensions.showReceivedMessageNotification
|
|
||||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||||
|
|
||||||
class SmsReceiver : BroadcastReceiver() {
|
class SmsReceiver : BroadcastReceiver() {
|
||||||
|
@ -34,6 +33,11 @@ class SmsReceiver : BroadcastReceiver() {
|
||||||
val messageId = context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
|
val messageId = context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
|
||||||
context.showReceivedMessageNotification(address, body, threadId.toInt(), null, messageId, false)
|
context.showReceivedMessageNotification(address, body, threadId.toInt(), null, messageId, false)
|
||||||
refreshMessages()
|
refreshMessages()
|
||||||
|
|
||||||
|
ensureBackgroundThread {
|
||||||
|
val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread
|
||||||
|
context.conversationsDB.insertOrUpdate(conversation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.simplemobiletools.smsmessenger.services
|
||||||
|
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import com.klinker.android.send_message.Settings
|
import com.klinker.android.send_message.Settings
|
||||||
import com.klinker.android.send_message.Transaction
|
import com.klinker.android.send_message.Transaction
|
||||||
import com.simplemobiletools.smsmessenger.extensions.getThreadId
|
import com.simplemobiletools.smsmessenger.extensions.getThreadId
|
||||||
|
@ -15,7 +16,7 @@ class HeadlessSmsSendService : Service() {
|
||||||
return START_NOT_STICKY
|
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 text = intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||||
val settings = Settings()
|
val settings = Settings()
|
||||||
settings.useSystemSending = true
|
settings.useSystemSending = true
|
||||||
|
|
|
@ -42,12 +42,12 @@
|
||||||
android:id="@+id/no_contacts_placeholder"
|
android:id="@+id/no_contacts_placeholder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/message_divider_two"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:layout_marginTop="@dimen/bigger_margin"
|
android:layout_marginTop="@dimen/bigger_margin"
|
||||||
android:alpha="0.8"
|
android:alpha="0.8"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:paddingLeft="@dimen/activity_margin"
|
android:paddingLeft="@dimen/activity_margin"
|
||||||
android:layout_below="@+id/message_divider_two"
|
|
||||||
android:paddingRight="@dimen/activity_margin"
|
android:paddingRight="@dimen/activity_margin"
|
||||||
android:text="@string/no_access_to_contacts"
|
android:text="@string/no_access_to_contacts"
|
||||||
android:textSize="@dimen/bigger_text_size"
|
android:textSize="@dimen/bigger_text_size"
|
|
@ -161,6 +161,7 @@
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:hint="@string/type_a_message"
|
android:hint="@string/type_a_message"
|
||||||
|
android:inputType="textCapSentences|textMultiLine"
|
||||||
android:minHeight="@dimen/normal_icon_size" />
|
android:minHeight="@dimen/normal_icon_size" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
android:icon="@drawable/ic_add_person_vector"
|
android:icon="@drawable/ic_add_person_vector"
|
||||||
android:title="@string/add_number_to_contact"
|
android:title="@string/add_number_to_contact"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/cab_block_number"
|
||||||
|
android:icon="@drawable/ic_minus_circle_vector"
|
||||||
|
android:title="@string/block_number"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/cab_select_all"
|
android:id="@+id/cab_select_all"
|
||||||
android:icon="@drawable/ic_select_all_vector"
|
android:icon="@drawable/ic_select_all_vector"
|
||||||
|
|
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 977 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,72 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Schlichter SMS Messenger</string>
|
||||||
|
<string name="app_launcher_name">SMS Messenger</string>
|
||||||
|
<string name="type_a_message">schreibe eine Nachricht…</string>
|
||||||
|
<string name="message_not_sent">Nachricht wurde noch nicht gesendet</string>
|
||||||
|
<string name="add_person">füge eine Person hinzu</string>
|
||||||
|
<string name="attachment">Anhang</string>
|
||||||
|
<string name="no_conversations_found">keine gespeicherten Chats gefunden</string>
|
||||||
|
<string name="start_conversation">einen neuen Chat beginnen</string>
|
||||||
|
|
||||||
|
<!-- New conversation -->
|
||||||
|
<string name="new_conversation">neuer Chat</string>
|
||||||
|
<string name="add_contact_or_number">Füge einen Kontakt oder eine Nummer hinzu…</string>
|
||||||
|
<string name="suggestions">Vorschläge</string>
|
||||||
|
|
||||||
|
<!-- Notifications -->
|
||||||
|
<string name="channel_received_sms">Empfangene SMS</string>
|
||||||
|
<string name="new_message">neue Nachricht</string>
|
||||||
|
<string name="mark_as_read">markiere als gelesen</string>
|
||||||
|
<string name="mark_as_unread">Mark as Unread</string>
|
||||||
|
|
||||||
|
<!-- Confirmation dialog -->
|
||||||
|
<string name="delete_whole_conversation_confirmation">Möchtest du wirklich alle Nachrichten dieses Chat löschen?</string>
|
||||||
|
|
||||||
|
<!-- Are you sure you want to delete 5 conversations? -->
|
||||||
|
<plurals name="delete_conversations">
|
||||||
|
<item quantity="one">%d Chat</item>
|
||||||
|
<item quantity="other">%d Chats</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<!-- Are you sure you want to delete 5 messages? -->
|
||||||
|
<plurals name="delete_messages">
|
||||||
|
<item quantity="one">%d Nachricht</item>
|
||||||
|
<item quantity="other">%d Nachrichten</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<!-- FAQ -->
|
||||||
|
<string name="faq_1_title">Why does the app require access to the internet?</string>
|
||||||
|
<string name="faq_1_text">Sadly it is needed for sending MMS attachments. Not being able to send MMS would be a really huge disadvantage compared to other apps, so we decided to go this way.
|
||||||
|
However, as usually, there are no ads, tracking or analytics whatsoever, the internet is used only for sending 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 - Manage messages easily</string>
|
||||||
|
<!-- Short description has to have max 80 characters -->
|
||||||
|
<string name="app_short_description">An easy and quick way of managing SMS and MMS messages without ads.</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>
|
|
@ -0,0 +1,72 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Απλός SMS Messenger</string>
|
||||||
|
<string name="app_launcher_name">SMS Messenger</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>
|
||||||
|
|
||||||
|
<!-- 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">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">Γιατί η εφαρμογή απαιτεί πρόσβαση στο Internet;</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">Απλός SMS Messenger - Εύκολη διαχείριση μηνυμάτων</string>
|
||||||
|
<!-- Short description has to have max 80 characters -->
|
||||||
|
<string name="app_short_description">Ένας εύκολος και γρήγορος τρόπος διαχείρισης SMS και MMS χωρίς διαφημίσεις.</string>
|
||||||
|
<string name="app_long_description">
|
||||||
|
Ένας εξαιρετικός τρόπος για να μείνετε σε επαφή με τους συγγενείς σας, στέλνοντας μηνύματα SMS και MMS. Η εφαρμογή χειρίζεται σωστά και την ομαδική ανταλλαγή μηνυμάτων, όπως και τον αποκλεισμό αριθμών από Android 7+.
|
||||||
|
|
||||||
|
Προσφέρει πολλές μορφές ημερομηνίας για να διαλέξετε, ώστε να αισθάνεστε άνετα να τις χρησιμοποιήσετε. Μπορείτε επίσης να κάνετε εναλλαγή μεταξύ 12 και 24 ωρών.
|
||||||
|
|
||||||
|
Έχει πραγματικά πολύ μικρό μέγεθος εφαρμογής σε σύγκριση με τον ανταγωνισμό, καθιστώντας την πραγματικά γρήγορη στη λήψη της.
|
||||||
|
|
||||||
|
Διατίθεται με σχεδίαση υλικού και σκούρο θέμα από προεπιλογή, παρέχει εξαιρετική εμπειρία χρήστη για εύκολη χρήση. Η έλλειψη πρόσβασης στο διαδίκτυο σάς παρέχει περισσότερο απόρρητο, ασφάλεια και σταθερότητα από ό,τι άλλες εφαρμογές.
|
||||||
|
|
||||||
|
Δεν περιέχει διαφημίσεις ή περιττά δικαιώματα. Είναι πλήρως ανοιχτού κώδικα, παρέχει προσαρμόσιμα χρώματα.
|
||||||
|
|
||||||
|
<b>Δείτε την πλήρη σειρά 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>
|
|
@ -0,0 +1,72 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Mensajería SMS Simple</string>
|
||||||
|
<string name="app_launcher_name">Mensajería SMS</string>
|
||||||
|
<string name="type_a_message">Escribe un mensaje…</string>
|
||||||
|
<string name="message_not_sent">El mensaje no se ha enviado.</string>
|
||||||
|
<string name="add_person">Añadir persona</string>
|
||||||
|
<string name="attachment">Archivo adjunto</string>
|
||||||
|
<string name="no_conversations_found">No se han encontrado conversaciones</string>
|
||||||
|
<string name="start_conversation">Inicia una conversación</string>
|
||||||
|
|
||||||
|
<!-- New conversation -->
|
||||||
|
<string name="new_conversation">Nueva conversación</string>
|
||||||
|
<string name="add_contact_or_number">Escribe contacto o número…</string>
|
||||||
|
<string name="suggestions">Sugerencias</string>
|
||||||
|
|
||||||
|
<!-- Notifications -->
|
||||||
|
<string name="channel_received_sms">Mensaje recibico</string>
|
||||||
|
<string name="new_message">Nuevo mensaje</string>
|
||||||
|
<string name="mark_as_read">Marcar como leído</string>
|
||||||
|
<string name="mark_as_unread">Mark as Unread</string>
|
||||||
|
|
||||||
|
<!-- Confirmation dialog -->
|
||||||
|
<string name="delete_whole_conversation_confirmation">¿Estás seguro que quieres eliminar todos los mensajes en esta conversación?</string>
|
||||||
|
|
||||||
|
<!-- Are you sure you want to delete 5 conversations? -->
|
||||||
|
<plurals name="delete_conversations">
|
||||||
|
<item quantity="one">%d conversación</item>
|
||||||
|
<item quantity="other">%d conversaciones</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<!-- Are you sure you want to delete 5 messages? -->
|
||||||
|
<plurals name="delete_messages">
|
||||||
|
<item quantity="one">%d mensaje</item>
|
||||||
|
<item quantity="other">%d mensajes</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<!-- FAQ -->
|
||||||
|
<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.
|
||||||
|
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 -->
|
||||||
|
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
|
||||||
|
<string name="app_title">Mensajería SMS Simple - Mensajea fácilmente</string>
|
||||||
|
<!-- Short description has to have max 80 characters -->
|
||||||
|
<string name="app_short_description">Una forma fácil y rápida de enviar mensajes SMS y MMS sin anuncios.</string>
|
||||||
|
<string name="app_long_description">
|
||||||
|
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.
|
||||||
|
|
||||||
|
<b>Mira la suite completa de herramientas Simples aquí:</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>
|
|
@ -17,6 +17,7 @@
|
||||||
<string name="channel_received_sms">Gautos žinutės</string>
|
<string name="channel_received_sms">Gautos žinutės</string>
|
||||||
<string name="new_message">Nauja žinutė</string>
|
<string name="new_message">Nauja žinutė</string>
|
||||||
<string name="mark_as_read">Mark as Read</string>
|
<string name="mark_as_read">Mark as Read</string>
|
||||||
|
<string name="mark_as_unread">Mark as Unread</string>
|
||||||
|
|
||||||
<!-- Confirmation dialog -->
|
<!-- Confirmation dialog -->
|
||||||
<string name="delete_whole_conversation_confirmation">Ar tikrai norite ištrinti visas šio pokalbio žinutes?</string>
|
<string name="delete_whole_conversation_confirmation">Ar tikrai norite ištrinti visas šio pokalbio žinutes?</string>
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Eenvoudig Berichtenbeheer (SMS)</string>
|
||||||
|
<string name="app_launcher_name">Berichten</string>
|
||||||
|
<string name="type_a_message">Typ een bericht…</string>
|
||||||
|
<string name="message_not_sent">Bericht niet verzonden.</string>
|
||||||
|
<string name="add_person">Persoon toevoegen</string>
|
||||||
|
<string name="attachment">Bijlage</string>
|
||||||
|
<string name="no_conversations_found">Geen opgeslagen berichten gevonden</string>
|
||||||
|
<string name="start_conversation">Een gesprek starten</string>
|
||||||
|
|
||||||
|
<!-- New conversation -->
|
||||||
|
<string name="new_conversation">Nieuw gesprek</string>
|
||||||
|
<string name="add_contact_or_number">Contact of nummer toevoegen…</string>
|
||||||
|
<string name="suggestions">Suggesties</string>
|
||||||
|
|
||||||
|
<!-- Notifications -->
|
||||||
|
<string name="channel_received_sms">Ontvangen berichten</string>
|
||||||
|
<string name="new_message">Nieuw bericht</string>
|
||||||
|
<string name="mark_as_read">Als gelezen markeren</string>
|
||||||
|
<string name="mark_as_unread">Als ongelezen markeren</string>
|
||||||
|
|
||||||
|
<!-- Confirmation dialog -->
|
||||||
|
<string name="delete_whole_conversation_confirmation">Alle berichten in dit gesprek verwijderen?</string>
|
||||||
|
|
||||||
|
<!-- Are you sure you want to delete 5 conversations? -->
|
||||||
|
<plurals name="delete_conversations">
|
||||||
|
<item quantity="one">%d gesperk</item>
|
||||||
|
<item quantity="other">%d gesprekken</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<!-- Are you sure you want to delete 5 messages? -->
|
||||||
|
<plurals name="delete_messages">
|
||||||
|
<item quantity="one">%d bericht</item>
|
||||||
|
<item quantity="other">%d berichten</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<!-- FAQ -->
|
||||||
|
<string name="faq_1_title">Waarom heeft deze app toegang nodig tot het internet?</string>
|
||||||
|
<string name="faq_1_text">Dit is helaas nodig voor het verzenden van MMS-bijlagen. Het versturen van MMS-berichten onmogelijk maken zou een te groot nadeel t.o.v. andere apps betekenen en daarom hebben we besloten om het toch toe te voegen.
|
||||||
|
Zoals gewoonlijk bevat de app echter geen advertenties, tracking of analytics; de verbinding wordt alleen maar gebruikt voor het versturen van MMS-berichten.</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">Eenvoudig Berichtenbeheer - Verstuur snel SMS/MMS</string>
|
||||||
|
<!-- Short description has to have max 80 characters -->
|
||||||
|
<string name="app_short_description">Beheer eenvoudig en snel SMS- en MMS-berichten, zonder advertenties.</string>
|
||||||
|
<string name="app_long_description">
|
||||||
|
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.
|
||||||
|
|
||||||
|
<b>Probeer ook eens de andere apps van Simple Tools:</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>
|
|
@ -16,7 +16,8 @@
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
<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">Mark as Read</string>
|
<string name="mark_as_read">Marcar como lida</string>
|
||||||
|
<string name="mark_as_unread">Mark as Unread</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>
|
||||||
|
@ -54,7 +55,7 @@
|
||||||
|
|
||||||
Não contém anúncios nem permissões desnecessárias. É open source e permite a personalização de cores.
|
Não contém anúncios nem permissões desnecessárias. É open source e permite a personalização de cores.
|
||||||
|
|
||||||
<b>Consulte o conjunto completo de aplicações Simple aqui:</b>
|
<b>Consulte o conjunto completo de aplicações Simple Tools aqui:</b>
|
||||||
https://www.simplemobiletools.com
|
https://www.simplemobiletools.com
|
||||||
|
|
||||||
<b>Facebook:</b>
|
<b>Facebook:</b>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
<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>
|
||||||
|
|
||||||
<!-- Confirmation dialog -->
|
<!-- Confirmation dialog -->
|
||||||
<string name="delete_whole_conversation_confirmation">Вы уверены, что хотите удалить все сообщения в этой переписке?</string>
|
<string name="delete_whole_conversation_confirmation">Вы уверены, что хотите удалить все сообщения в этой переписке?</string>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
<string name="channel_received_sms">Prijatá SMS</string>
|
<string name="channel_received_sms">Prijatá SMS</string>
|
||||||
<string name="new_message">Nová správa</string>
|
<string name="new_message">Nová správa</string>
|
||||||
<string name="mark_as_read">Označiť ako prečítané</string>
|
<string name="mark_as_read">Označiť ako prečítané</string>
|
||||||
|
<string name="mark_as_unread">Označiť ako neprečítané</string>
|
||||||
|
|
||||||
<!-- Confirmation dialog -->
|
<!-- Confirmation dialog -->
|
||||||
<string name="delete_whole_conversation_confirmation">Ste si istý, že chcete odstrániť všetky správy tejto konverzácie?</string>
|
<string name="delete_whole_conversation_confirmation">Ste si istý, že chcete odstrániť všetky správy tejto konverzácie?</string>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
<string name="channel_received_sms">Received SMS</string>
|
<string name="channel_received_sms">Received SMS</string>
|
||||||
<string name="new_message">New message</string>
|
<string name="new_message">New message</string>
|
||||||
<string name="mark_as_read">Mark as Read</string>
|
<string name="mark_as_read">Mark as Read</string>
|
||||||
|
<string name="mark_as_unread">Mark as Unread</string>
|
||||||
|
|
||||||
<!-- Confirmation dialog -->
|
<!-- Confirmation dialog -->
|
||||||
<string name="delete_whole_conversation_confirmation">Are you sure you want to delete all messages of this conversation?</string>
|
<string name="delete_whole_conversation_confirmation">Are you sure you want to delete all messages of this conversation?</string>
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
Ένας εξαιρετικός τρόπος για να μείνετε σε επαφή με τους συγγενείς σας, στέλνοντας μηνύματα SMS και MMS. Η εφαρμογή χειρίζεται σωστά και την ομαδική ανταλλαγή μηνυμάτων, όπως και τον αποκλεισμό αριθμών από Android 7+.
|
||||||
|
|
||||||
|
Προσφέρει πολλές μορφές ημερομηνίας για να διαλέξετε, ώστε να αισθάνεστε άνετα να τις χρησιμοποιήσετε. Μπορείτε επίσης να κάνετε εναλλαγή μεταξύ 12 και 24 ωρών.
|
||||||
|
|
||||||
|
Έχει πραγματικά πολύ μικρό μέγεθος εφαρμογής σε σύγκριση με τον ανταγωνισμό, καθιστώντας την πραγματικά γρήγορη στη λήψη της.
|
||||||
|
|
||||||
|
Διατίθεται με σχεδίαση υλικού και σκούρο θέμα από προεπιλογή, παρέχει εξαιρετική εμπειρία χρήστη για εύκολη χρήση. Η έλλειψη πρόσβασης στο διαδίκτυο σάς παρέχει περισσότερο απόρρητο, ασφάλεια και σταθερότητα από ό,τι άλλες εφαρμογές.
|
||||||
|
|
||||||
|
Δεν περιέχει διαφημίσεις ή περιττά δικαιώματα. Είναι πλήρως ανοιχτού κώδικα, παρέχει προσαρμόσιμα χρώματα.
|
||||||
|
|
||||||
|
<b>Δείτε την πλήρη σειρά 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
|
|
@ -0,0 +1 @@
|
||||||
|
Ένας εύκολος και γρήγορος τρόπος διαχείρισης SMS και MMS χωρίς διαφημίσεις.
|
|
@ -0,0 +1 @@
|
||||||
|
Απλός SMS Messenger - Εύκολη διαχείριση μηνυμάτων
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
||||||
|
|
||||||
|
<b>Mira la suite completa de herramientas Simples aquí:</b>
|
||||||
|
https://www.simplemobiletools.com
|
||||||
|
|
||||||
|
<b>Facebook:</b>
|
||||||
|
https://www.facebook.com/simplemobiletools
|
||||||
|
|
||||||
|
<b>Reddit:</b>
|
||||||
|
https://www.reddit.com/r/SimpleMobileTools
|
|
@ -0,0 +1 @@
|
||||||
|
Una forma fácil y rápida de enviar mensajes SMS y MMS sin anuncios.
|
|
@ -0,0 +1 @@
|
||||||
|
Mensajería SMS Simple - Mensajea fácilmente
|
|
@ -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.
|
Não contém anúncios nem permissões desnecessárias. É open source e permite a personalização de cores.
|
||||||
|
|
||||||
<b>Consulte o conjunto completo de aplicações Simple aqui:</b>
|
<b>Consulte o conjunto completo de aplicações Simple Tools aqui:</b>
|
||||||
https://www.simplemobiletools.com
|
https://www.simplemobiletools.com
|
||||||
|
|
||||||
<b>Facebook:</b>
|
<b>Facebook:</b>
|
||||||
|
|