22
CHANGELOG.md
@@ -1,6 +1,28 @@
|
|||||||
Changelog
|
Changelog
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
Version 5.1.1 *(2020-05-08)*
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
* Allow moving the app on an SD card
|
||||||
|
* Added some performance, UI and stability improvements
|
||||||
|
|
||||||
|
Version 5.1.0 *(2020-05-03)*
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
* Adding multi-SIM support
|
||||||
|
* Properly show the latest messages, not the oldest ones in some cases
|
||||||
|
* Increased minimal OS version to Android 5.1
|
||||||
|
* Fixed many other UI and UX related issues
|
||||||
|
|
||||||
|
Version 5.0.1 *(2020-04-19)*
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
* Properly handle incoming multipart SMS messages
|
||||||
|
* Fixed a couple coloring issues
|
||||||
|
* Do not allow attempts to block a number below Android 7
|
||||||
|
* A couple other translation, stability and UI improvements
|
||||||
|
|
||||||
Version 5.0.0 *(2020-04-14)*
|
Version 5.0.0 *(2020-04-14)*
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
@@ -14,10 +14,10 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.simplemobiletools.smsmessenger"
|
applicationId "com.simplemobiletools.smsmessenger"
|
||||||
minSdkVersion 21
|
minSdkVersion 22
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 1
|
versionCode 4
|
||||||
versionName "5.0.0"
|
versionName "5.1.1"
|
||||||
setProperty("archivesBaseName", "sms-messenger")
|
setProperty("archivesBaseName", "sms-messenger")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,8 +56,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.simplemobiletools:commons:5.25.19'
|
implementation 'com.simplemobiletools:commons:5.27.24'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
|
|
||||||
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'
|
||||||
|
27
app/proguard-rules.pro
vendored
@@ -1,21 +1,6 @@
|
|||||||
# Add project specific ProGuard rules here.
|
# EventBus
|
||||||
# You can control the set of applied configuration files using the
|
-keepattributes *Annotation*
|
||||||
# proguardFiles setting in build.gradle.
|
-keepclassmembers class ** {
|
||||||
#
|
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||||
# For more details, see
|
}
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.simplemobiletools.smsmessenger">
|
package="com.simplemobiletools.smsmessenger"
|
||||||
|
android:installLocation="auto">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_SMS" />
|
<uses-permission android:name="android.permission.READ_SMS" />
|
||||||
<uses-permission android:name="android.permission.WRITE_SMS" />
|
<uses-permission android:name="android.permission.WRITE_SMS" />
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
<uses-permission android:name="android.permission.RECEIVE_MMS" />
|
<uses-permission android:name="android.permission.RECEIVE_MMS" />
|
||||||
<uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
|
<uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
|
@@ -48,11 +48,16 @@ class MainActivity : SimpleActivity() {
|
|||||||
|
|
||||||
if (isQPlus()) {
|
if (isQPlus()) {
|
||||||
val roleManager = getSystemService(RoleManager::class.java)
|
val roleManager = getSystemService(RoleManager::class.java)
|
||||||
if (roleManager!!.isRoleHeld(RoleManager.ROLE_SMS)) {
|
if (roleManager!!.isRoleAvailable(RoleManager.ROLE_SMS)) {
|
||||||
askPermissions()
|
if (roleManager.isRoleHeld(RoleManager.ROLE_SMS)) {
|
||||||
|
askPermissions()
|
||||||
|
} else {
|
||||||
|
val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS)
|
||||||
|
startActivityForResult(intent, MAKE_DEFAULT_APP_REQUEST)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS)
|
toast(R.string.unknown_error_occurred)
|
||||||
startActivityForResult(intent, MAKE_DEFAULT_APP_REQUEST)
|
finish()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Telephony.Sms.getDefaultSmsPackage(this) == packageName) {
|
if (Telephony.Sms.getDefaultSmsPackage(this) == packageName) {
|
||||||
|
@@ -3,23 +3,28 @@ package com.simplemobiletools.smsmessenger.activities
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
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.PERMISSION_READ_CONTACTS
|
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
|
||||||
|
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
|
import com.simplemobiletools.commons.models.SimpleContact
|
||||||
import com.simplemobiletools.smsmessenger.R
|
import com.simplemobiletools.smsmessenger.R
|
||||||
import com.simplemobiletools.smsmessenger.adapters.ContactsAdapter
|
import com.simplemobiletools.smsmessenger.adapters.ContactsAdapter
|
||||||
import com.simplemobiletools.smsmessenger.extensions.*
|
import com.simplemobiletools.smsmessenger.extensions.config
|
||||||
|
import com.simplemobiletools.smsmessenger.extensions.getSuggestedContacts
|
||||||
|
import com.simplemobiletools.smsmessenger.extensions.getThreadId
|
||||||
import com.simplemobiletools.smsmessenger.helpers.*
|
import com.simplemobiletools.smsmessenger.helpers.*
|
||||||
import com.simplemobiletools.smsmessenger.models.Contact
|
|
||||||
import kotlinx.android.synthetic.main.activity_conversation.*
|
import kotlinx.android.synthetic.main.activity_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.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class NewConversationActivity : SimpleActivity() {
|
class NewConversationActivity : SimpleActivity() {
|
||||||
private var allContacts = ArrayList<Contact>()
|
private var allContacts = ArrayList<SimpleContact>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -42,6 +47,11 @@ class NewConversationActivity : SimpleActivity() {
|
|||||||
no_contacts_placeholder_2.underlineText()
|
no_contacts_placeholder_2.underlineText()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
updateMenuItemColors(menu)
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
private fun initContacts() {
|
private fun initContacts() {
|
||||||
if (isThirdPartyIntent()) {
|
if (isThirdPartyIntent()) {
|
||||||
return
|
return
|
||||||
@@ -50,7 +60,7 @@ class NewConversationActivity : SimpleActivity() {
|
|||||||
fetchContacts()
|
fetchContacts()
|
||||||
new_conversation_address.onTextChangeListener {
|
new_conversation_address.onTextChangeListener {
|
||||||
val searchString = it
|
val searchString = it
|
||||||
val filteredContacts = ArrayList<Contact>()
|
val filteredContacts = ArrayList<SimpleContact>()
|
||||||
allContacts.forEach {
|
allContacts.forEach {
|
||||||
if (it.phoneNumber.contains(searchString, true) || it.name.contains(searchString, true)) {
|
if (it.phoneNumber.contains(searchString, true) || it.name.contains(searchString, true)) {
|
||||||
filteredContacts.add(it)
|
filteredContacts.add(it)
|
||||||
@@ -85,7 +95,7 @@ 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.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(number, "")
|
launchThreadActivity(URLDecoder.decode(number), "")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -93,7 +103,7 @@ class NewConversationActivity : SimpleActivity() {
|
|||||||
|
|
||||||
private fun fetchContacts() {
|
private fun fetchContacts() {
|
||||||
fillSuggestedContacts {
|
fillSuggestedContacts {
|
||||||
getAvailableContacts {
|
SimpleContactsHelper(this).getAvailableContacts {
|
||||||
allContacts = it
|
allContacts = it
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
@@ -103,7 +113,7 @@ class NewConversationActivity : SimpleActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupAdapter(contacts: ArrayList<Contact>) {
|
private fun setupAdapter(contacts: ArrayList<SimpleContact>) {
|
||||||
val hasContacts = contacts.isNotEmpty()
|
val hasContacts = contacts.isNotEmpty()
|
||||||
contacts_list.beVisibleIf(hasContacts)
|
contacts_list.beVisibleIf(hasContacts)
|
||||||
no_contacts_placeholder.beVisibleIf(!hasContacts)
|
no_contacts_placeholder.beVisibleIf(!hasContacts)
|
||||||
@@ -116,7 +126,7 @@ class NewConversationActivity : SimpleActivity() {
|
|||||||
|
|
||||||
ContactsAdapter(this, contacts, contacts_list, null) {
|
ContactsAdapter(this, contacts, contacts_list, null) {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
launchThreadActivity((it as Contact).phoneNumber, it.name)
|
launchThreadActivity((it as SimpleContact).phoneNumber, it.name)
|
||||||
}.apply {
|
}.apply {
|
||||||
contacts_list.adapter = this
|
contacts_list.adapter = this
|
||||||
}
|
}
|
||||||
@@ -139,7 +149,7 @@ 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
|
||||||
loadImage(contact.photoUri, suggested_contact_image, contact.name)
|
SimpleContactsHelper(this@NewConversationActivity).loadContactImage(contact.photoUri, suggested_contact_image, contact.name)
|
||||||
suggestions_holder.addView(this)
|
suggestions_holder.addView(this)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
launchThreadActivity(contact.phoneNumber, contact.name)
|
launchThreadActivity(contact.phoneNumber, contact.name)
|
||||||
@@ -152,15 +162,11 @@ class NewConversationActivity : SimpleActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupLetterFastscroller(contacts: ArrayList<Contact>) {
|
private fun setupLetterFastscroller(contacts: ArrayList<SimpleContact>) {
|
||||||
contacts_letter_fastscroller.setupWithRecyclerView(contacts_list, { position ->
|
contacts_letter_fastscroller.setupWithRecyclerView(contacts_list, { position ->
|
||||||
try {
|
try {
|
||||||
val name = contacts[position].name
|
val name = contacts[position].name
|
||||||
var character = if (name.isNotEmpty()) name.substring(0, 1) else ""
|
val character = if (name.isNotEmpty()) name.substring(0, 1) else ""
|
||||||
if (!character.areLettersOnly()) {
|
|
||||||
character = "#"
|
|
||||||
}
|
|
||||||
|
|
||||||
FastScrollItemIndicator.Text(character.toUpperCase(Locale.getDefault()))
|
FastScrollItemIndicator.Text(character.toUpperCase(Locale.getDefault()))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
FastScrollItemIndicator.Text("")
|
FastScrollItemIndicator.Text("")
|
||||||
|
@@ -4,6 +4,7 @@ import android.annotation.TargetApi
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
import com.simplemobiletools.commons.activities.ManageBlockedNumbersActivity
|
import com.simplemobiletools.commons.activities.ManageBlockedNumbersActivity
|
||||||
import com.simplemobiletools.commons.dialogs.ChangeDateTimeFormatDialog
|
import com.simplemobiletools.commons.dialogs.ChangeDateTimeFormatDialog
|
||||||
import com.simplemobiletools.commons.extensions.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
@@ -15,7 +16,7 @@ import kotlinx.android.synthetic.main.activity_settings.*
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SettingsActivity : SimpleActivity() {
|
class SettingsActivity : SimpleActivity() {
|
||||||
var blockedNumbersAtPause = -1
|
private var blockedNumbersAtPause = -1
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -42,6 +43,11 @@ class SettingsActivity : SimpleActivity() {
|
|||||||
blockedNumbersAtPause = getBlockedNumbers().hashCode()
|
blockedNumbersAtPause = getBlockedNumbers().hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
updateMenuItemColors(menu)
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupPurchaseThankYou() {
|
private fun setupPurchaseThankYou() {
|
||||||
settings_purchase_thank_you_holder.beVisibleIf(!isThankYouInstalled())
|
settings_purchase_thank_you_holder.beVisibleIf(!isThankYouInstalled())
|
||||||
settings_purchase_thank_you_holder.setOnClickListener {
|
settings_purchase_thank_you_holder.setOnClickListener {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package com.simplemobiletools.smsmessenger.activities
|
package com.simplemobiletools.smsmessenger.activities
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
@@ -8,6 +9,7 @@ import android.media.MediaMetadataRetriever
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Telephony
|
import android.provider.Telephony
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
@@ -28,7 +30,11 @@ 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.SimpleContactsHelper
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
|
import com.simplemobiletools.commons.helpers.isNougatPlus
|
||||||
|
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
|
||||||
import com.simplemobiletools.smsmessenger.adapters.ThreadAdapter
|
import com.simplemobiletools.smsmessenger.adapters.ThreadAdapter
|
||||||
@@ -47,10 +53,12 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
private val PICK_ATTACHMENT_INTENT = 1
|
private val PICK_ATTACHMENT_INTENT = 1
|
||||||
|
|
||||||
private var threadId = 0
|
private var threadId = 0
|
||||||
|
private var currentSIMCardIndex = 0
|
||||||
private var threadItems = ArrayList<ThreadItem>()
|
private var threadItems = ArrayList<ThreadItem>()
|
||||||
private var bus: EventBus? = null
|
private var bus: EventBus? = null
|
||||||
private var participants = ArrayList<Contact>()
|
private var participants = ArrayList<SimpleContact>()
|
||||||
private var messages = ArrayList<Message>()
|
private var messages = ArrayList<Message>()
|
||||||
|
private val availableSIMCards = ArrayList<SIMCard>()
|
||||||
private var attachmentUris = LinkedHashSet<Uri>()
|
private var attachmentUris = LinkedHashSet<Uri>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -71,7 +79,16 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
|
|
||||||
bus = EventBus.getDefault()
|
bus = EventBus.getDefault()
|
||||||
bus!!.register(this)
|
bus!!.register(this)
|
||||||
|
handlePermission(PERMISSION_READ_PHONE_STATE) {
|
||||||
|
if (it) {
|
||||||
|
setupThread()
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupThread() {
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
messages = getMessages(threadId)
|
messages = getMessages(threadId)
|
||||||
participants = if (messages.isEmpty()) {
|
participants = if (messages.isEmpty()) {
|
||||||
@@ -89,7 +106,7 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
return@ensureBackgroundThread
|
return@ensureBackgroundThread
|
||||||
}
|
}
|
||||||
|
|
||||||
val contact = Contact(0, name, "", number)
|
val contact = SimpleContact(0, 0, name, "", number)
|
||||||
participants.add(contact)
|
participants.add(contact)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +149,8 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||||
thread_type_message.requestFocus()
|
thread_type_message.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupSIMSelector()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setupButtons()
|
setupButtons()
|
||||||
@@ -146,8 +165,10 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
menuInflater.inflate(R.menu.menu_thread, menu)
|
menuInflater.inflate(R.menu.menu_thread, menu)
|
||||||
menu.apply {
|
menu.apply {
|
||||||
findItem(R.id.delete).isVisible = threadItems.isNotEmpty()
|
findItem(R.id.delete).isVisible = threadItems.isNotEmpty()
|
||||||
|
findItem(R.id.block_number).isVisible = isNougatPlus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateMenuItemColors(menu)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +202,7 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
thread_messages_list.adapter = adapter
|
thread_messages_list.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
getAvailableContacts {
|
SimpleContactsHelper(this).getAvailableContacts {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
val adapter = AutoCompleteTextViewAdapter(this, it)
|
val adapter = AutoCompleteTextViewAdapter(this, it)
|
||||||
add_contact_or_number.setAdapter(adapter)
|
add_contact_or_number.setAdapter(adapter)
|
||||||
@@ -200,16 +221,17 @@ 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 = Contact(number.hashCode(), number, "", number)
|
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", number)
|
||||||
addSelectedContact(contact)
|
addSelectedContact(contact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupButtons() {
|
private fun setupButtons() {
|
||||||
updateTextColors(thread_holder)
|
updateTextColors(thread_holder)
|
||||||
thread_send_message.applyColorFilter(config.textColor)
|
val textColor = config.textColor
|
||||||
confirm_manage_contacts.applyColorFilter(config.textColor)
|
thread_send_message.applyColorFilter(textColor)
|
||||||
thread_add_attachment.applyColorFilter(config.textColor)
|
confirm_manage_contacts.applyColorFilter(textColor)
|
||||||
|
thread_add_attachment.applyColorFilter(textColor)
|
||||||
|
|
||||||
thread_send_message.setOnClickListener {
|
thread_send_message.setOnClickListener {
|
||||||
sendMessage()
|
sendMessage()
|
||||||
@@ -250,6 +272,40 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
private fun setupSIMSelector() {
|
||||||
|
val availableSIMs = SubscriptionManager.from(this).activeSubscriptionInfoList
|
||||||
|
if (availableSIMs.size > 1) {
|
||||||
|
availableSIMs.forEachIndexed { index, subscriptionInfo ->
|
||||||
|
var label = subscriptionInfo.displayName.toString()
|
||||||
|
if (subscriptionInfo.number?.isNotEmpty() == true) {
|
||||||
|
label += " (${subscriptionInfo.number})"
|
||||||
|
}
|
||||||
|
val SIMCard = SIMCard(index + 1, subscriptionInfo.subscriptionId, label)
|
||||||
|
availableSIMCards.add(SIMCard)
|
||||||
|
}
|
||||||
|
|
||||||
|
val numbers = participants.map { it.phoneNumber }.toTypedArray()
|
||||||
|
currentSIMCardIndex = availableSIMs.indexOfFirstOrNull { it.subscriptionId == config.getUseSIMIdAtNumber(numbers.first()) } ?: 0
|
||||||
|
|
||||||
|
thread_select_sim_icon.applyColorFilter(config.textColor)
|
||||||
|
thread_select_sim_icon.beVisible()
|
||||||
|
thread_select_sim_number.beVisible()
|
||||||
|
|
||||||
|
if (availableSIMCards.isNotEmpty()) {
|
||||||
|
thread_select_sim_icon.setOnClickListener {
|
||||||
|
currentSIMCardIndex = (currentSIMCardIndex + 1) % availableSIMCards.size
|
||||||
|
val currentSIMCard = availableSIMCards[currentSIMCardIndex]
|
||||||
|
thread_select_sim_number.text = currentSIMCard.id.toString()
|
||||||
|
toast(currentSIMCard.label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_select_sim_number.setTextColor(config.textColor.getContrastColor())
|
||||||
|
thread_select_sim_number.text = (availableSIMCards[currentSIMCardIndex].id).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun blockNumber() {
|
private fun blockNumber() {
|
||||||
val baseString = R.string.block_confirmation
|
val baseString = R.string.block_confirmation
|
||||||
val numbers = participants.map { it.phoneNumber }.toTypedArray()
|
val numbers = participants.map { it.phoneNumber }.toTypedArray()
|
||||||
@@ -294,8 +350,8 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
layoutInflater.inflate(R.layout.item_selected_contact, null).apply {
|
layoutInflater.inflate(R.layout.item_selected_contact, null).apply {
|
||||||
selected_contact_name.text = contact.name
|
selected_contact_name.text = contact.name
|
||||||
selected_contact_remove.setOnClickListener {
|
selected_contact_remove.setOnClickListener {
|
||||||
if (contact.id != participants.first().id) {
|
if (contact.rawId != participants.first().rawId) {
|
||||||
removeSelectedContact(contact.id)
|
removeSelectedContact(contact.rawId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
views.add(this)
|
views.add(this)
|
||||||
@@ -304,9 +360,9 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
showSelectedContact(views)
|
showSelectedContact(views)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addSelectedContact(contact: Contact) {
|
private fun addSelectedContact(contact: SimpleContact) {
|
||||||
add_contact_or_number.setText("")
|
add_contact_or_number.setText("")
|
||||||
if (participants.map { it.id }.contains(contact.id)) {
|
if (participants.map { it.rawId }.contains(contact.rawId)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,6 +475,15 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
val numbers = participants.map { it.phoneNumber }.toTypedArray()
|
val numbers = participants.map { it.phoneNumber }.toTypedArray()
|
||||||
val settings = Settings()
|
val settings = Settings()
|
||||||
settings.useSystemSending = true
|
settings.useSystemSending = true
|
||||||
|
|
||||||
|
val SIMId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId
|
||||||
|
if (SIMId != null) {
|
||||||
|
settings.subscriptionId = SIMId
|
||||||
|
numbers.forEach {
|
||||||
|
config.saveUseSIMIdAtNumber(it, SIMId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -430,12 +495,16 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.sendNewMessage(message, threadId.toLong())
|
try {
|
||||||
|
transaction.sendNewMessage(message, threadId.toLong())
|
||||||
|
|
||||||
thread_type_message.setText("")
|
thread_type_message.setText("")
|
||||||
attachmentUris.clear()
|
attachmentUris.clear()
|
||||||
thread_attachments_holder.beGone()
|
thread_attachments_holder.beGone()
|
||||||
thread_attachments_wrapper.removeAllViews()
|
thread_attachments_wrapper.removeAllViews()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showErrorToast(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// show selected contacts, properly split to new lines when appropriate
|
// show selected contacts, properly split to new lines when appropriate
|
||||||
@@ -488,7 +557,7 @@ class ThreadActivity : SimpleActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeSelectedContact(id: Int) {
|
private fun removeSelectedContact(id: Int) {
|
||||||
participants = participants.filter { it.id != id }.toMutableList() as ArrayList<Contact>
|
participants = participants.filter { it.rawId != id }.toMutableList() as ArrayList<SimpleContact>
|
||||||
showSelectedContacts()
|
showSelectedContacts()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,30 +5,30 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Filter
|
import android.widget.Filter
|
||||||
|
import android.widget.TextView
|
||||||
import com.simplemobiletools.commons.extensions.normalizeString
|
import com.simplemobiletools.commons.extensions.normalizeString
|
||||||
|
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||||
|
import com.simplemobiletools.commons.models.SimpleContact
|
||||||
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.extensions.loadImage
|
|
||||||
import com.simplemobiletools.smsmessenger.models.Contact
|
|
||||||
import kotlinx.android.synthetic.main.item_contact.view.*
|
|
||||||
|
|
||||||
class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: ArrayList<Contact>) :
|
class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: ArrayList<SimpleContact>) : ArrayAdapter<SimpleContact>(activity, 0, contacts) {
|
||||||
ArrayAdapter<Contact>(activity, 0, contacts) {
|
|
||||||
var resultList = ArrayList<Contact>()
|
var resultList = ArrayList<SimpleContact>()
|
||||||
|
|
||||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
val contact = resultList[position]
|
val contact = resultList[position]
|
||||||
var listItem = convertView
|
var listItem = convertView
|
||||||
if (listItem == null || listItem.tag != contact.name.isNotEmpty()) {
|
if (listItem == null || listItem.tag != contact.name.isNotEmpty()) {
|
||||||
listItem = LayoutInflater.from(activity).inflate(R.layout.item_contact, parent, false)
|
listItem = LayoutInflater.from(activity).inflate(R.layout.item_contact_with_number, parent, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
listItem!!.apply {
|
listItem!!.apply {
|
||||||
tag = contact.name.isNotEmpty()
|
tag = contact.name.isNotEmpty()
|
||||||
item_autocomplete_name.text = contact.name
|
findViewById<TextView>(R.id.item_contact_name).text = contact.name
|
||||||
item_autocomplete_number.text = contact.phoneNumber
|
findViewById<TextView>(R.id.item_contact_number).text = contact.phoneNumber
|
||||||
|
|
||||||
context.loadImage(contact.photoUri, item_autocomplete_image, contact.name)
|
SimpleContactsHelper(context).loadContactImage(contact.photoUri, findViewById(R.id.item_contact_image), contact.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return listItem
|
return listItem
|
||||||
@@ -62,7 +62,7 @@ class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: Ar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun convertResultToString(resultValue: Any?) = (resultValue as? Contact)?.name
|
override fun convertResultToString(resultValue: Any?) = (resultValue as? SimpleContact)?.name
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItem(index: Int) = resultList[index]
|
override fun getItem(index: Int) = resultList[index]
|
||||||
|
@@ -3,22 +3,20 @@ package com.simplemobiletools.smsmessenger.adapters
|
|||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
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.helpers.SimpleContactsHelper
|
||||||
|
import com.simplemobiletools.commons.models.SimpleContact
|
||||||
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
|
||||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||||
import com.simplemobiletools.smsmessenger.extensions.loadImage
|
|
||||||
import com.simplemobiletools.smsmessenger.models.Contact
|
|
||||||
import kotlinx.android.synthetic.main.item_contact_with_number.view.*
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ContactsAdapter(
|
class ContactsAdapter(activity: SimpleActivity, var contacts: ArrayList<SimpleContact>, recyclerView: MyRecyclerView, fastScroller: FastScroller?,
|
||||||
activity: SimpleActivity, var contacts: ArrayList<Contact>, recyclerView: MyRecyclerView,
|
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
||||||
fastScroller: FastScroller?, itemClick: (Any) -> Unit
|
|
||||||
) :
|
|
||||||
MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
|
||||||
|
|
||||||
override fun getActionMenuId() = 0
|
override fun getActionMenuId() = 0
|
||||||
|
|
||||||
@@ -30,9 +28,9 @@ class ContactsAdapter(
|
|||||||
|
|
||||||
override fun getIsItemSelectable(position: Int) = true
|
override fun getIsItemSelectable(position: Int) = true
|
||||||
|
|
||||||
override fun getItemSelectionKey(position: Int) = contacts.getOrNull(position)?.id
|
override fun getItemSelectionKey(position: Int) = contacts.getOrNull(position)?.rawId
|
||||||
|
|
||||||
override fun getItemKeyPosition(key: Int) = contacts.indexOfFirst { it.id == key }
|
override fun getItemKeyPosition(key: Int) = contacts.indexOfFirst { it.rawId == key }
|
||||||
|
|
||||||
override fun onActionModeCreated() {}
|
override fun onActionModeCreated() {}
|
||||||
|
|
||||||
@@ -53,19 +51,19 @@ class ContactsAdapter(
|
|||||||
override fun onViewRecycled(holder: ViewHolder) {
|
override fun onViewRecycled(holder: ViewHolder) {
|
||||||
super.onViewRecycled(holder)
|
super.onViewRecycled(holder)
|
||||||
if (!activity.isDestroyed && !activity.isFinishing) {
|
if (!activity.isDestroyed && !activity.isFinishing) {
|
||||||
Glide.with(activity).clear(holder.itemView.contact_tmb)
|
Glide.with(activity).clear(holder.itemView.findViewById<ImageView>(R.id.item_contact_image))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupView(view: View, contact: Contact) {
|
private fun setupView(view: View, contact: SimpleContact) {
|
||||||
view.apply {
|
view.apply {
|
||||||
contact_name.text = contact.name
|
findViewById<TextView>(R.id.item_contact_name).text = contact.name
|
||||||
contact_name.setTextColor(textColor)
|
findViewById<TextView>(R.id.item_contact_name).setTextColor(textColor)
|
||||||
|
|
||||||
contact_number.text = contact.phoneNumber
|
findViewById<TextView>(R.id.item_contact_number).text = contact.phoneNumber
|
||||||
contact_number.setTextColor(textColor)
|
findViewById<TextView>(R.id.item_contact_number).setTextColor(textColor)
|
||||||
|
|
||||||
context.loadImage(contact.photoUri, contact_tmb, contact.name)
|
SimpleContactsHelper(context).loadContactImage(contact.photoUri, findViewById(R.id.item_contact_image), contact.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,24 +9,19 @@ 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.formatDateOrTime
|
import com.simplemobiletools.commons.extensions.formatDateOrTime
|
||||||
import com.simplemobiletools.commons.extensions.getColoredGroupIcon
|
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
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
|
||||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||||
import com.simplemobiletools.smsmessenger.extensions.deleteConversation
|
import com.simplemobiletools.smsmessenger.extensions.deleteConversation
|
||||||
import com.simplemobiletools.smsmessenger.extensions.loadImage
|
|
||||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||||
import kotlinx.android.synthetic.main.item_conversation.view.*
|
import kotlinx.android.synthetic.main.item_conversation.view.*
|
||||||
|
|
||||||
class ConversationsAdapter(
|
class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayList<Conversation>, recyclerView: MyRecyclerView, fastScroller: FastScroller,
|
||||||
activity: SimpleActivity, var conversations: ArrayList<Conversation>,
|
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
||||||
recyclerView: MyRecyclerView,
|
|
||||||
fastScroller: FastScroller,
|
|
||||||
itemClick: (Any) -> Unit
|
|
||||||
) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setupDragListener(true)
|
setupDragListener(true)
|
||||||
@@ -141,12 +136,12 @@ class ConversationsAdapter(
|
|||||||
|
|
||||||
// at group conversations we use an icon as the placeholder, not any letter
|
// at group conversations we use an icon as the placeholder, not any letter
|
||||||
val placeholder = if (conversation.isGroupConversation) {
|
val placeholder = if (conversation.isGroupConversation) {
|
||||||
activity.getColoredGroupIcon(conversation.title)
|
SimpleContactsHelper(context).getColoredGroupIcon(conversation.title)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
context.loadImage(conversation.photoUri, conversation_image, conversation.title, placeholder)
|
SimpleContactsHelper(context).loadContactImage(conversation.photoUri, conversation_image, conversation.title, placeholder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,13 +20,13 @@ import com.bumptech.glide.request.target.Target
|
|||||||
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.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
|
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
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
|
||||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||||
import com.simplemobiletools.smsmessenger.extensions.deleteMessage
|
import com.simplemobiletools.smsmessenger.extensions.deleteMessage
|
||||||
import com.simplemobiletools.smsmessenger.extensions.loadImage
|
|
||||||
import com.simplemobiletools.smsmessenger.helpers.*
|
import com.simplemobiletools.smsmessenger.helpers.*
|
||||||
import com.simplemobiletools.smsmessenger.models.Message
|
import com.simplemobiletools.smsmessenger.models.Message
|
||||||
import com.simplemobiletools.smsmessenger.models.ThreadDateTime
|
import com.simplemobiletools.smsmessenger.models.ThreadDateTime
|
||||||
@@ -38,12 +38,8 @@ 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.*
|
||||||
|
|
||||||
class ThreadAdapter(
|
class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem>, recyclerView: MyRecyclerView, fastScroller: FastScroller,
|
||||||
activity: SimpleActivity, var messages: ArrayList<ThreadItem>,
|
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
||||||
recyclerView: MyRecyclerView,
|
|
||||||
fastScroller: FastScroller,
|
|
||||||
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()
|
||||||
|
|
||||||
@@ -186,7 +182,7 @@ class ThreadAdapter(
|
|||||||
if (message.isReceivedMessage()) {
|
if (message.isReceivedMessage()) {
|
||||||
thread_message_sender_photo.beVisible()
|
thread_message_sender_photo.beVisible()
|
||||||
thread_message_body.setTextColor(textColor)
|
thread_message_body.setTextColor(textColor)
|
||||||
context.loadImage(message.senderPhotoUri, thread_message_sender_photo, message.senderName)
|
SimpleContactsHelper(context).loadContactImage(message.senderPhotoUri, thread_message_sender_photo, message.senderName)
|
||||||
} else {
|
} else {
|
||||||
thread_message_sender_photo?.beGone()
|
thread_message_sender_photo?.beGone()
|
||||||
val background = context.getAdjustedPrimaryColor()
|
val background = context.getAdjustedPrimaryColor()
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package com.simplemobiletools.smsmessenger.extensions
|
package com.simplemobiletools.smsmessenger.extensions
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import com.simplemobiletools.smsmessenger.models.Contact
|
import com.simplemobiletools.commons.models.SimpleContact
|
||||||
|
|
||||||
fun ArrayList<Contact>.getThreadTitle() = TextUtils.join(", ", map { it.name }.toTypedArray())
|
fun ArrayList<SimpleContact>.getThreadTitle() = TextUtils.join(", ", map { it.name }.toTypedArray())
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
package com.simplemobiletools.smsmessenger.extensions
|
||||||
|
|
||||||
|
inline fun <T> List<T>.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? {
|
||||||
|
var index = 0
|
||||||
|
for (item in this) {
|
||||||
|
if (predicate(item))
|
||||||
|
return index
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
@@ -9,27 +9,17 @@ import android.content.ContentValues
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.media.AudioAttributes
|
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.provider.ContactsContract
|
|
||||||
import android.provider.ContactsContract.CommonDataKinds
|
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Organization
|
|
||||||
import android.provider.ContactsContract.CommonDataKinds.StructuredName
|
|
||||||
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
|
||||||
import android.widget.ImageView
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
|
||||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
|
||||||
import com.bumptech.glide.request.RequestOptions
|
|
||||||
import com.simplemobiletools.commons.extensions.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
import com.simplemobiletools.commons.helpers.*
|
import com.simplemobiletools.commons.helpers.*
|
||||||
|
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.helpers.Config
|
import com.simplemobiletools.smsmessenger.helpers.Config
|
||||||
@@ -54,12 +44,22 @@ fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
|||||||
|
|
||||||
val selection = "${Sms.THREAD_ID} = ?"
|
val selection = "${Sms.THREAD_ID} = ?"
|
||||||
val selectionArgs = arrayOf(threadId.toString())
|
val selectionArgs = arrayOf(threadId.toString())
|
||||||
val sortOrder = "${Sms._ID} LIMIT 100"
|
val sortOrder = "${Sms._ID} DESC LIMIT 100"
|
||||||
|
|
||||||
|
val blockStatus = HashMap<String, Boolean>()
|
||||||
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)
|
val senderNumber = cursor.getStringValue(Sms.ADDRESS)
|
||||||
if (isNumberBlocked(senderNumber)) {
|
|
||||||
|
val isNumberBlocked = if (blockStatus.containsKey(senderNumber)) {
|
||||||
|
blockStatus[senderNumber]!!
|
||||||
|
} else {
|
||||||
|
val isBlocked = isNumberBlocked(senderNumber)
|
||||||
|
blockStatus[senderNumber] = isBlocked
|
||||||
|
isBlocked
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNumberBlocked) {
|
||||||
return@queryCursor
|
return@queryCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
|||||||
val date = (cursor.getLongValue(Sms.DATE) / 1000).toInt()
|
val date = (cursor.getLongValue(Sms.DATE) / 1000).toInt()
|
||||||
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 participant = Contact(0, senderName, photoUri, senderNumber)
|
val participant = SimpleContact(0, 0, senderName, photoUri, senderNumber)
|
||||||
val isMMS = false
|
val isMMS = false
|
||||||
val message = Message(id, body, type, arrayListOf(participant), date, read, thread, isMMS, null, senderName, photoUri)
|
val message = Message(id, body, type, arrayListOf(participant), date, read, thread, isMMS, null, senderName, photoUri)
|
||||||
messages.add(message)
|
messages.add(message)
|
||||||
@@ -85,7 +85,7 @@ fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
|||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
// as soon as a message contains multiple recipients it count as an MMS instead of SMS
|
// as soon as a message contains multiple recipients it counts as an MMS instead of SMS
|
||||||
fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<Message> {
|
fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<Message> {
|
||||||
val uri = Mms.CONTENT_URI
|
val uri = Mms.CONTENT_URI
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
@@ -109,14 +109,22 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<
|
|||||||
}
|
}
|
||||||
|
|
||||||
val messages = ArrayList<Message>()
|
val messages = ArrayList<Message>()
|
||||||
val contactsMap = HashMap<Int, Contact>()
|
val contactsMap = HashMap<Int, SimpleContact>()
|
||||||
|
val threadParticipants = HashMap<Int, ArrayList<SimpleContact>>()
|
||||||
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
|
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
|
||||||
val mmsId = cursor.getIntValue(Mms._ID)
|
val mmsId = cursor.getIntValue(Mms._ID)
|
||||||
val type = cursor.getIntValue(Mms.MESSAGE_BOX)
|
val type = cursor.getIntValue(Mms.MESSAGE_BOX)
|
||||||
val date = cursor.getLongValue(Mms.DATE).toInt()
|
val date = cursor.getLongValue(Mms.DATE).toInt()
|
||||||
val read = cursor.getIntValue(Mms.READ) == 1
|
val read = cursor.getIntValue(Mms.READ) == 1
|
||||||
val threadId = cursor.getIntValue(Mms.THREAD_ID)
|
val threadId = cursor.getIntValue(Mms.THREAD_ID)
|
||||||
val participants = getThreadParticipants(threadId, contactsMap)
|
val participants = if (threadParticipants.containsKey(threadId)) {
|
||||||
|
threadParticipants[threadId]!!
|
||||||
|
} else {
|
||||||
|
val parts = getThreadParticipants(threadId, contactsMap)
|
||||||
|
threadParticipants[threadId] = parts
|
||||||
|
parts
|
||||||
|
}
|
||||||
|
|
||||||
val isMMS = true
|
val isMMS = true
|
||||||
val attachment = getMmsAttachment(mmsId)
|
val attachment = getMmsAttachment(mmsId)
|
||||||
val body = attachment?.text ?: ""
|
val body = attachment?.text ?: ""
|
||||||
@@ -136,7 +144,7 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<
|
|||||||
messages.add(message)
|
messages.add(message)
|
||||||
|
|
||||||
participants.forEach {
|
participants.forEach {
|
||||||
contactsMap.put(it.id, it)
|
contactsMap.put(it.rawId, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +204,7 @@ fun Context.getConversations(): ArrayList<Conversation> {
|
|||||||
|
|
||||||
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) getPhotoUriFromPhoneNumber(phoneNumbers.first()) else ""
|
val photoUri = if (phoneNumbers.size == 1) SimpleContactsHelper(this).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)
|
val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation)
|
||||||
conversations.add(conversation)
|
conversations.add(conversation)
|
||||||
@@ -279,14 +287,14 @@ fun Context.getThreadSnippet(threadId: Int): String {
|
|||||||
return snippet
|
return snippet
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getThreadParticipants(threadId: Int, contactsMap: HashMap<Int, Contact>?): ArrayList<Contact> {
|
fun Context.getThreadParticipants(threadId: Int, contactsMap: HashMap<Int, SimpleContact>?): ArrayList<SimpleContact> {
|
||||||
val uri = Uri.parse("${MmsSms.CONTENT_CONVERSATIONS_URI}?simple=true")
|
val uri = Uri.parse("${MmsSms.CONTENT_CONVERSATIONS_URI}?simple=true")
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
ThreadsColumns.RECIPIENT_IDS
|
ThreadsColumns.RECIPIENT_IDS
|
||||||
)
|
)
|
||||||
val selection = "${Mms._ID} = ?"
|
val selection = "${Mms._ID} = ?"
|
||||||
val selectionArgs = arrayOf(threadId.toString())
|
val selectionArgs = arrayOf(threadId.toString())
|
||||||
val participants = ArrayList<Contact>()
|
val participants = ArrayList<SimpleContact>()
|
||||||
try {
|
try {
|
||||||
val cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
|
val cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||||
cursor?.use {
|
cursor?.use {
|
||||||
@@ -303,7 +311,7 @@ fun Context.getThreadParticipants(threadId: Int, contactsMap: HashMap<Int, Conta
|
|||||||
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 = Contact(addressId, name, photoUri, phoneNumber)
|
val contact = SimpleContact(addressId, addressId, name, photoUri, phoneNumber)
|
||||||
participants.add(contact)
|
participants.add(contact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +333,7 @@ fun Context.getThreadPhoneNumbers(recipientIds: List<Int>): ArrayList<String> {
|
|||||||
fun Context.getThreadContactNames(phoneNumbers: List<String>): ArrayList<String> {
|
fun Context.getThreadContactNames(phoneNumbers: List<String>): ArrayList<String> {
|
||||||
val names = ArrayList<String>()
|
val names = ArrayList<String>()
|
||||||
phoneNumbers.forEach {
|
phoneNumbers.forEach {
|
||||||
names.add(getNameFromPhoneNumber(it))
|
names.add(SimpleContactsHelper(this).getNameFromPhoneNumber(it))
|
||||||
}
|
}
|
||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
@@ -351,8 +359,8 @@ fun Context.getPhoneNumberFromAddressId(canonicalAddressId: Int): String {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getSuggestedContacts(): ArrayList<Contact> {
|
fun Context.getSuggestedContacts(): ArrayList<SimpleContact> {
|
||||||
val contacts = ArrayList<Contact>()
|
val contacts = ArrayList<SimpleContact>()
|
||||||
val uri = Sms.CONTENT_URI
|
val uri = Sms.CONTENT_URI
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
Sms.ADDRESS
|
Sms.ADDRESS
|
||||||
@@ -371,7 +379,7 @@ fun Context.getSuggestedContacts(): ArrayList<Contact> {
|
|||||||
|
|
||||||
val senderName = namePhoto.name
|
val senderName = namePhoto.name
|
||||||
val photoUri = namePhoto.photoUri ?: ""
|
val photoUri = namePhoto.photoUri ?: ""
|
||||||
val contact = Contact(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)
|
||||||
}
|
}
|
||||||
@@ -380,34 +388,6 @@ fun Context.getSuggestedContacts(): ArrayList<Contact> {
|
|||||||
return contacts
|
return contacts
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getAvailableContacts(callback: (ArrayList<Contact>) -> Unit) {
|
|
||||||
ensureBackgroundThread {
|
|
||||||
val names = getContactNames()
|
|
||||||
var allContacts = getContactPhoneNumbers()
|
|
||||||
allContacts.forEach {
|
|
||||||
val contactId = it.id
|
|
||||||
val contact = names.firstOrNull { it.id == contactId }
|
|
||||||
val name = contact?.name
|
|
||||||
if (name != null) {
|
|
||||||
it.name = name
|
|
||||||
}
|
|
||||||
|
|
||||||
val photoUri = contact?.photoUri
|
|
||||||
if (photoUri != null) {
|
|
||||||
it.photoUri = photoUri
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allContacts = allContacts.filter { it.name.isNotEmpty() }.distinctBy {
|
|
||||||
val startIndex = Math.max(0, it.phoneNumber.length - 9)
|
|
||||||
it.phoneNumber.substring(startIndex)
|
|
||||||
}.toMutableList() as ArrayList<Contact>
|
|
||||||
|
|
||||||
allContacts.sortBy { it.name.normalizeString().toLowerCase(Locale.getDefault()) }
|
|
||||||
callback(allContacts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto? {
|
fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto? {
|
||||||
if (!hasPermission(PERMISSION_READ_CONTACTS)) {
|
if (!hasPermission(PERMISSION_READ_CONTACTS)) {
|
||||||
return NamePhoto(number, null)
|
return NamePhoto(number, null)
|
||||||
@@ -435,128 +415,6 @@ fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto? {
|
|||||||
return NamePhoto(number, null)
|
return NamePhoto(number, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getNameFromPhoneNumber(number: String): String {
|
|
||||||
if (!hasPermission(PERMISSION_READ_CONTACTS)) {
|
|
||||||
return number
|
|
||||||
}
|
|
||||||
|
|
||||||
val uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number))
|
|
||||||
val projection = arrayOf(
|
|
||||||
PhoneLookup.DISPLAY_NAME
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val cursor = contentResolver.query(uri, projection, null, null, null)
|
|
||||||
cursor.use {
|
|
||||||
if (cursor?.moveToFirst() == true) {
|
|
||||||
return cursor.getStringValue(PhoneLookup.DISPLAY_NAME)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
showErrorToast(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
return number
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.getPhotoUriFromPhoneNumber(number: String): String {
|
|
||||||
if (!hasPermission(PERMISSION_READ_CONTACTS)) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
val uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number))
|
|
||||||
val projection = arrayOf(
|
|
||||||
PhoneLookup.PHOTO_URI
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val cursor = contentResolver.query(uri, projection, null, null, null)
|
|
||||||
cursor.use {
|
|
||||||
if (cursor?.moveToFirst() == true) {
|
|
||||||
return cursor.getStringValue(PhoneLookup.PHOTO_URI) ?: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
showErrorToast(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.getContactNames(): List<Contact> {
|
|
||||||
val contacts = ArrayList<Contact>()
|
|
||||||
val uri = ContactsContract.Data.CONTENT_URI
|
|
||||||
val projection = arrayOf(
|
|
||||||
ContactsContract.Data.CONTACT_ID,
|
|
||||||
StructuredName.PREFIX,
|
|
||||||
StructuredName.GIVEN_NAME,
|
|
||||||
StructuredName.MIDDLE_NAME,
|
|
||||||
StructuredName.FAMILY_NAME,
|
|
||||||
StructuredName.SUFFIX,
|
|
||||||
StructuredName.PHOTO_THUMBNAIL_URI,
|
|
||||||
Organization.COMPANY,
|
|
||||||
Organization.TITLE,
|
|
||||||
ContactsContract.Data.MIMETYPE
|
|
||||||
)
|
|
||||||
|
|
||||||
val selection = "${ContactsContract.Data.MIMETYPE} = ? OR ${ContactsContract.Data.MIMETYPE} = ?"
|
|
||||||
val selectionArgs = arrayOf(
|
|
||||||
StructuredName.CONTENT_ITEM_TYPE,
|
|
||||||
Organization.CONTENT_ITEM_TYPE
|
|
||||||
)
|
|
||||||
|
|
||||||
queryCursor(uri, projection, selection, selectionArgs) { cursor ->
|
|
||||||
val id = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
|
|
||||||
val mimetype = cursor.getStringValue(ContactsContract.Data.MIMETYPE)
|
|
||||||
val photoUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
|
|
||||||
val isPerson = mimetype == StructuredName.CONTENT_ITEM_TYPE
|
|
||||||
if (isPerson) {
|
|
||||||
val prefix = cursor.getStringValue(StructuredName.PREFIX) ?: ""
|
|
||||||
val firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: ""
|
|
||||||
val middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: ""
|
|
||||||
val familyName = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: ""
|
|
||||||
val suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: ""
|
|
||||||
if (firstName.isNotEmpty() || middleName.isNotEmpty() || familyName.isNotEmpty()) {
|
|
||||||
val names = arrayOf(prefix, firstName, middleName, familyName, suffix).filter { it.isNotEmpty() }
|
|
||||||
val fullName = TextUtils.join(" ", names)
|
|
||||||
val contact = Contact(id, fullName, photoUri, "")
|
|
||||||
contacts.add(contact)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val isOrganization = mimetype == Organization.CONTENT_ITEM_TYPE
|
|
||||||
if (isOrganization) {
|
|
||||||
val company = cursor.getStringValue(Organization.COMPANY) ?: ""
|
|
||||||
val jobTitle = cursor.getStringValue(Organization.TITLE) ?: ""
|
|
||||||
if (company.isNotEmpty() || jobTitle.isNotEmpty()) {
|
|
||||||
val fullName = "$company $jobTitle".trim()
|
|
||||||
val contact = Contact(id, fullName, photoUri, "")
|
|
||||||
contacts.add(contact)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contacts
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.getContactPhoneNumbers(): ArrayList<Contact> {
|
|
||||||
val contacts = ArrayList<Contact>()
|
|
||||||
val uri = CommonDataKinds.Phone.CONTENT_URI
|
|
||||||
val projection = arrayOf(
|
|
||||||
ContactsContract.Data.CONTACT_ID,
|
|
||||||
CommonDataKinds.Phone.NORMALIZED_NUMBER
|
|
||||||
)
|
|
||||||
|
|
||||||
queryCursor(uri, projection) { cursor ->
|
|
||||||
val id = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
|
|
||||||
val phoneNumber = cursor.getStringValue(CommonDataKinds.Phone.NORMALIZED_NUMBER)
|
|
||||||
if (phoneNumber != null) {
|
|
||||||
val contact = Contact(id, "", "", phoneNumber)
|
|
||||||
contacts.add(contact)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contacts
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.insertNewSMS(address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int) {
|
fun Context.insertNewSMS(address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int) {
|
||||||
val uri = Sms.CONTENT_URI
|
val uri = Sms.CONTENT_URI
|
||||||
val contentValues = ContentValues().apply {
|
val contentValues = ContentValues().apply {
|
||||||
@@ -619,6 +477,10 @@ fun Context.getThreadId(addresses: Set<String>): Long {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Context.isNumberBlocked(number: String): Boolean {
|
fun Context.isNumberBlocked(number: String): Boolean {
|
||||||
|
if (!isNougatPlus()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
val blockedNumbers = getBlockedNumbers()
|
val blockedNumbers = getBlockedNumbers()
|
||||||
val numberToCompare = number.trimToComparableNumber()
|
val numberToCompare = number.trimToComparableNumber()
|
||||||
return blockedNumbers.map { it.numberToCompare }.contains(numberToCompare) || blockedNumbers.map { it.number }.contains(numberToCompare)
|
return blockedNumbers.map { it.numberToCompare }.contains(numberToCompare) || blockedNumbers.map { it.number }.contains(numberToCompare)
|
||||||
@@ -655,7 +517,7 @@ fun Context.showReceivedMessageNotification(address: String, body: String, threa
|
|||||||
val summaryText = getString(R.string.new_message)
|
val summaryText = getString(R.string.new_message)
|
||||||
val sender = getNameAndPhotoFromPhoneNumber(address)?.name ?: ""
|
val sender = getNameAndPhotoFromPhoneNumber(address)?.name ?: ""
|
||||||
|
|
||||||
val largeIcon = bitmap ?: getContactLetterIcon(sender)
|
val largeIcon = bitmap ?: SimpleContactsHelper(this).getContactLetterIcon(sender)
|
||||||
val builder = NotificationCompat.Builder(this, channelId)
|
val builder = NotificationCompat.Builder(this, channelId)
|
||||||
.setContentTitle(sender)
|
.setContentTitle(sender)
|
||||||
.setContentText(body)
|
.setContentText(body)
|
||||||
@@ -672,20 +534,3 @@ fun Context.showReceivedMessageNotification(address: String, body: String, threa
|
|||||||
|
|
||||||
notificationManager.notify(threadID, builder.build())
|
notificationManager.notify(threadID, builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.loadImage(path: String, imageView: ImageView, placeholderName: String, placeholderImage: Drawable? = null) {
|
|
||||||
val placeholder = placeholderImage ?: BitmapDrawable(resources, getContactLetterIcon(placeholderName))
|
|
||||||
|
|
||||||
val options = RequestOptions()
|
|
||||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
|
||||||
.error(placeholder)
|
|
||||||
.centerCrop()
|
|
||||||
|
|
||||||
Glide.with(this)
|
|
||||||
.load(path)
|
|
||||||
.transition(DrawableTransitionOptions.withCrossFade())
|
|
||||||
.placeholder(placeholder)
|
|
||||||
.apply(options)
|
|
||||||
.apply(RequestOptions.circleCropTransform())
|
|
||||||
.into(imageView)
|
|
||||||
}
|
|
||||||
|
@@ -7,4 +7,10 @@ class Config(context: Context) : BaseConfig(context) {
|
|||||||
companion object {
|
companion object {
|
||||||
fun newInstance(context: Context) = Config(context)
|
fun newInstance(context: Context) = Config(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveUseSIMIdAtNumber(number: String, SIMId: Int) {
|
||||||
|
prefs.edit().putInt(USE_SIM_ID_PREFIX + number, SIMId).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUseSIMIdAtNumber(number: String) = prefs.getInt(USE_SIM_ID_PREFIX + number, 0)
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ const val THREAD_TEXT = "thread_text"
|
|||||||
const val THREAD_NUMBER = "thread_number"
|
const val THREAD_NUMBER = "thread_number"
|
||||||
const val THREAD_ATTACHMENT_URI = "thread_attachment_uri"
|
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_"
|
||||||
|
|
||||||
// view types for the thread list view
|
// view types for the thread list view
|
||||||
const val THREAD_DATE_TIME = 1
|
const val THREAD_DATE_TIME = 1
|
||||||
@@ -16,10 +17,6 @@ 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
|
||||||
|
|
||||||
// constants used at passing info to SmsSentReceiver
|
|
||||||
const val MESSAGE_BODY = "message_body"
|
|
||||||
const val MESSAGE_ADDRESS = "message_address"
|
|
||||||
|
|
||||||
fun refreshMessages() {
|
fun refreshMessages() {
|
||||||
EventBus.getDefault().post(Events.RefreshMessages())
|
EventBus.getDefault().post(Events.RefreshMessages())
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
package com.simplemobiletools.smsmessenger.models
|
|
||||||
|
|
||||||
data class Contact(val id: Int, var name: String, var photoUri: String, var phoneNumber: String)
|
|
@@ -1,10 +1,10 @@
|
|||||||
package com.simplemobiletools.smsmessenger.models
|
package com.simplemobiletools.smsmessenger.models
|
||||||
|
|
||||||
import android.provider.Telephony
|
import android.provider.Telephony
|
||||||
|
import com.simplemobiletools.commons.models.SimpleContact
|
||||||
|
|
||||||
data class Message(
|
data class Message(
|
||||||
val id: Int, val body: String, val type: Int, val participants: ArrayList<Contact>, 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 isMMS: Boolean, val attachment: MessageAttachment?, val senderName: String, val senderPhotoUri: String) : ThreadItem() {
|
||||||
) : ThreadItem() {
|
|
||||||
fun isReceivedMessage() = type == Telephony.Sms.MESSAGE_TYPE_INBOX
|
fun isReceivedMessage() = type == Telephony.Sms.MESSAGE_TYPE_INBOX
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
package com.simplemobiletools.smsmessenger.models
|
||||||
|
|
||||||
|
data class SIMCard(val id: Int, val subscriptionId: Int, val label: String)
|
@@ -13,22 +13,26 @@ import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
|||||||
class SmsReceiver : BroadcastReceiver() {
|
class SmsReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
|
val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
|
||||||
messages.forEach {
|
var address = ""
|
||||||
val address = it.originatingAddress ?: ""
|
var body = ""
|
||||||
if (context.isNumberBlocked(address)) {
|
var subject = ""
|
||||||
return@forEach
|
var date = 0L
|
||||||
}
|
var threadId = 0L
|
||||||
|
val type = Telephony.Sms.MESSAGE_TYPE_INBOX
|
||||||
|
val read = 0
|
||||||
|
|
||||||
val subject = it.pseudoSubject
|
messages.forEach {
|
||||||
val body = it.messageBody
|
address = it.originatingAddress ?: ""
|
||||||
val date = it.timestampMillis
|
subject = it.pseudoSubject
|
||||||
val threadId = context.getThreadId(address)
|
body += it.messageBody
|
||||||
val type = Telephony.Sms.MESSAGE_TYPE_INBOX
|
date = Math.min(it.timestampMillis, System.currentTimeMillis())
|
||||||
val read = 0
|
threadId = context.getThreadId(address)
|
||||||
context.insertNewSMS(address, subject, body, date, read, threadId, type)
|
|
||||||
context.showReceivedMessageNotification(address, body, threadId.toInt())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshMessages()
|
if (!context.isNumberBlocked(address)) {
|
||||||
|
context.insertNewSMS(address, subject, body, date, read, threadId, type)
|
||||||
|
context.showReceivedMessageNotification(address, body, threadId.toInt())
|
||||||
|
refreshMessages()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -156,13 +156,39 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:layout_marginEnd="@dimen/small_margin"
|
android:layout_marginEnd="@dimen/small_margin"
|
||||||
android:layout_toStartOf="@+id/thread_send_message"
|
android:layout_toStartOf="@+id/thread_select_sim_icon"
|
||||||
android:layout_toEndOf="@+id/thread_add_attachment"
|
android:layout_toEndOf="@+id/thread_add_attachment"
|
||||||
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:minHeight="@dimen/normal_icon_size" />
|
android:minHeight="@dimen/normal_icon_size" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/thread_select_sim_icon"
|
||||||
|
android:layout_width="@dimen/normal_icon_size"
|
||||||
|
android:layout_height="@dimen/normal_icon_size"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_marginTop="@dimen/small_margin"
|
||||||
|
android:layout_toStartOf="@+id/thread_send_message"
|
||||||
|
android:alpha="0.9"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:padding="@dimen/normal_margin"
|
||||||
|
android:src="@drawable/ic_sim_vector"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/thread_select_sim_number"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignStart="@+id/thread_select_sim_icon"
|
||||||
|
android:layout_alignTop="@+id/thread_select_sim_icon"
|
||||||
|
android:layout_alignEnd="@+id/thread_select_sim_icon"
|
||||||
|
android:layout_alignBottom="@+id/thread_select_sim_icon"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textSize="@dimen/normal_text_size"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="1" />
|
||||||
|
|
||||||
<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"
|
||||||
|
@@ -1,57 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/item_autocomplete_holder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="@dimen/small_margin"
|
|
||||||
android:paddingTop="@dimen/medium_margin"
|
|
||||||
android:paddingEnd="@dimen/medium_margin"
|
|
||||||
android:paddingBottom="@dimen/medium_margin">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/item_autocomplete_image"
|
|
||||||
android:layout_width="@dimen/avatar_size"
|
|
||||||
android:layout_height="@dimen/avatar_size"
|
|
||||||
android:layout_margin="@dimen/tiny_margin"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/item_autocomplete_name"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:lines="1"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:paddingStart="@dimen/medium_margin"
|
|
||||||
android:paddingEnd="@dimen/medium_margin"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textSize="@dimen/bigger_text_size"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/item_autocomplete_number"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/item_autocomplete_image"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Simple Mobile" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/item_autocomplete_number"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@+id/item_autocomplete_name"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:alpha="0.8"
|
|
||||||
android:lines="1"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:paddingStart="@dimen/medium_margin"
|
|
||||||
android:paddingEnd="@dimen/medium_margin"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textSize="@dimen/normal_text_size"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/item_autocomplete_image"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/item_autocomplete_name"
|
|
||||||
tools:text="123456" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/contact_holder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingTop="@dimen/medium_margin"
|
|
||||||
android:paddingEnd="@dimen/activity_margin"
|
|
||||||
android:paddingBottom="@dimen/medium_margin">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/contact_tmb"
|
|
||||||
android:layout_width="@dimen/normal_icon_size"
|
|
||||||
android:layout_height="@dimen/normal_icon_size"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginStart="@dimen/small_margin"
|
|
||||||
android:layout_marginEnd="@dimen/small_margin"
|
|
||||||
android:padding="@dimen/small_margin"
|
|
||||||
android:src="@drawable/ic_person_vector" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/contact_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_toEndOf="@+id/contact_tmb"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textSize="@dimen/big_text_size"
|
|
||||||
tools:text="John Doe" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/contact_number"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@+id/contact_name"
|
|
||||||
android:layout_alignStart="@+id/contact_name"
|
|
||||||
android:layout_toEndOf="@+id/contact_tmb"
|
|
||||||
android:alpha="0.6"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textSize="@dimen/normal_text_size"
|
|
||||||
tools:text="0123 456 789" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/thread_message_sender_photo"
|
android:id="@+id/thread_message_sender_photo"
|
||||||
android:layout_width="@dimen/avatar_size"
|
android:layout_width="@dimen/list_avatar_size"
|
||||||
android:layout_height="@dimen/avatar_size"
|
android:layout_height="@dimen/list_avatar_size"
|
||||||
android:layout_alignBottom="@+id/thread_message_body"
|
android:layout_alignBottom="@+id/thread_message_body"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginEnd="@dimen/medium_margin"
|
android:layout_marginEnd="@dimen/medium_margin"
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
android:id="@+id/thread_mesage_attachments_holder"
|
android:id="@+id/thread_mesage_attachments_holder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignStart="@+id/thread_message_body"
|
android:layout_toEndOf="@+id/thread_message_sender_photo"
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
android:layout_alignBottom="@+id/thread_mesage_attachments_holder"
|
android:layout_alignBottom="@+id/thread_mesage_attachments_holder"
|
||||||
android:layout_marginStart="@dimen/medium_margin"
|
android:layout_marginStart="@dimen/medium_margin"
|
||||||
android:layout_marginBottom="@dimen/activity_margin"
|
android:layout_marginBottom="@dimen/activity_margin"
|
||||||
android:src="@drawable/ic_play_outline"
|
android:src="@drawable/ic_play_outline_vector"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@@ -2,9 +2,7 @@
|
|||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/thread_received_attachment_wrapper"
|
android:id="@+id/thread_received_attachment_wrapper"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:paddingTop="@dimen/medium_margin"
|
|
||||||
android:paddingBottom="@dimen/medium_margin">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/thread_received_attachment_label"
|
android:id="@+id/thread_received_attachment_label"
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
android:layout_alignBottom="@+id/thread_mesage_attachments_holder"
|
android:layout_alignBottom="@+id/thread_mesage_attachments_holder"
|
||||||
android:layout_marginEnd="@dimen/medium_margin"
|
android:layout_marginEnd="@dimen/medium_margin"
|
||||||
android:layout_marginBottom="@dimen/activity_margin"
|
android:layout_marginBottom="@dimen/activity_margin"
|
||||||
android:src="@drawable/ic_play_outline"
|
android:src="@drawable/ic_play_outline_vector"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.1 KiB |
70
app/src/main/res/values-lt/strings.xml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Paprastas SMS Siuntiklis</string>
|
||||||
|
<string name="app_launcher_name">SMS Siuntiklis</string>
|
||||||
|
<string name="type_a_message">Rašykite žinutę…</string>
|
||||||
|
<string name="message_not_sent">Žinutė neišsiųsta.</string>
|
||||||
|
<string name="add_person">Pridėti žmogų</string>
|
||||||
|
<string name="attachment">Priedas</string>
|
||||||
|
<string name="no_conversations_found">Nebuvo rasta išsaugotų pokalbių</string>
|
||||||
|
<string name="start_conversation">Pradėtipokalbį</string>
|
||||||
|
|
||||||
|
<!-- New conversation -->
|
||||||
|
<string name="new_conversation">Naujas pokalbis</string>
|
||||||
|
<string name="add_contact_or_number">Pridėti kontaktą arba numerį…</string>
|
||||||
|
<string name="suggestions">Pasiūlymai</string>
|
||||||
|
|
||||||
|
<!-- Notifications -->
|
||||||
|
<string name="channel_received_sms">Gautos žinutės</string>
|
||||||
|
<string name="new_message">Nauja žinutė</string>
|
||||||
|
<string name="mark_as_read">Mark as Read</string>
|
||||||
|
|
||||||
|
<!-- Confirmation dialog -->
|
||||||
|
<string name="delete_whole_conversation_confirmation">Ar tikrai norite ištrinti visas šio pokalbio žinutes?</string>
|
||||||
|
|
||||||
|
<!-- Are you sure you want to delete 5 conversations? -->
|
||||||
|
<plurals name="delete_conversations">
|
||||||
|
<item quantity="one">%d pokalbis</item>
|
||||||
|
<item quantity="other">%d pokalbiai</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<!-- Are you sure you want to delete 5 messages? -->
|
||||||
|
<plurals name="delete_messages">
|
||||||
|
<item quantity="one">%d žinutė</item>
|
||||||
|
<item quantity="other">%d žinutės</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.
|
||||||
|
|
||||||
|
Neturi reklamų ar nereikalingų leidimų. Programėlė visiškai atviro kodo, yra galimybė keisti spalvas.
|
||||||
|
<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>
|
@@ -4,18 +4,19 @@
|
|||||||
<string name="type_a_message">Escreva uma mensagem…</string>
|
<string name="type_a_message">Escreva 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">Attachment</string>
|
<string name="attachment">Anexo</string>
|
||||||
<string name="no_conversations_found">No stored conversations have been found</string>
|
<string name="no_conversations_found">Não foram encontradas conversas</string>
|
||||||
<string name="start_conversation">Start a conversation</string>
|
<string name="start_conversation">Iniciar uma conversa</string>
|
||||||
|
|
||||||
<!-- New message -->
|
<!-- New message -->
|
||||||
<string name="new_conversation">New conversation</string>
|
<string name="new_conversation">Nova conversa</string>
|
||||||
<string name="add_contact_or_number">Adicionar contacto ou número…</string>
|
<string name="add_contact_or_number">Adicionar contacto ou número…</string>
|
||||||
<string name="suggestions">Sugestões</string>
|
<string name="suggestions">Sugestões</string>
|
||||||
|
|
||||||
<!-- 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>
|
||||||
|
|
||||||
<!-- 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>
|
||||||
@@ -39,21 +40,21 @@
|
|||||||
|
|
||||||
<!-- 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 -->
|
||||||
<string name="app_title">Simple SMS Messenger - Manage messages easily</string>
|
<string name="app_title">Simple SMS Messenger - Gestão de mensagens</string>
|
||||||
<!-- Short description has to have max 80 characters -->
|
<!-- 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_short_description">Aplicação simples para gerir SMS e MMS, sem anúncios.</string>
|
||||||
<string name="app_long_description">
|
<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+.
|
Uma excelente forma para manter o contacto com os seus amigos e familiares. Também pode ser utilizada para mensagens de grupo e possibilita bloqueio de números de telefone em versões 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.
|
Permite a utilização de vários formatos de data. Também pode alternar o formato das horas.
|
||||||
|
|
||||||
It has a really tiny app size compared to the competition, making it really fast to download.
|
É uma aplicação pequena - comparada com as aplicações do mesmo género - o que torna a sua descarga muito rápida.
|
||||||
|
|
||||||
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.
|
Disponibiliza um design atrativo e um tema escuro por omissão. A não utilização da permissão Internet providencia-lhe mais privacidade, segurança e estabilidade do que as outras aplicações.
|
||||||
|
|
||||||
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
|
Não contém anúncios nem permissões desnecessárias. É open source e permite a personalização de cores.
|
||||||
|
|
||||||
<b>Check out the full suite of Simple Tools here:</b>
|
<b>Consulte o conjunto completo de aplicações Simple aqui:</b>
|
||||||
https://www.simplemobiletools.com
|
https://www.simplemobiletools.com
|
||||||
|
|
||||||
<b>Facebook:</b>
|
<b>Facebook:</b>
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
<!-- 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">Mark as Read</string>
|
||||||
|
|
||||||
<!-- Confirmation dialog -->
|
<!-- Confirmation dialog -->
|
||||||
<string name="delete_whole_conversation_confirmation">Вы уверены, что хотите удалить все сообщения в этой переписке?</string>
|
<string name="delete_whole_conversation_confirmation">Вы уверены, что хотите удалить все сообщения в этой переписке?</string>
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
<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>
|
||||||
|
|
||||||
<!-- 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>
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<dimen name="notification_large_icon_size">72dp</dimen>
|
<dimen name="notification_large_icon_size">72dp</dimen>
|
||||||
<dimen name="avatar_size">40dp</dimen>
|
|
||||||
<dimen name="bigger_avatar_size">64dp</dimen>
|
<dimen name="bigger_avatar_size">64dp</dimen>
|
||||||
<dimen name="play_outline_size">36dp</dimen>
|
<dimen name="play_outline_size">36dp</dimen>
|
||||||
<dimen name="attachment_preview_size">60dp</dimen>
|
<dimen name="attachment_preview_size">60dp</dimen>
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
<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>
|
||||||
|
|
||||||
<!-- 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>
|
||||||
|
@@ -8,7 +8,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||||
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
|
||||||
|
4
fastlane/metadata/android/en-US/changelogs/2.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
* Properly handle incoming multipart SMS messages
|
||||||
|
* Fixed a couple coloring issues
|
||||||
|
* Do not allow attempts to block a number below Android 7
|
||||||
|
* A couple other translation, stability and UI improvements
|
4
fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
* Adding multi-SIM support
|
||||||
|
* Properly show the latest messages, not the oldest ones in some cases
|
||||||
|
* Increased minimal OS version to Android 5.1
|
||||||
|
* Fixed many other UI and UX related issues
|
2
fastlane/metadata/android/en-US/changelogs/4.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
* Allow moving the app on an SD card
|
||||||
|
* Added some performance, UI and stability improvements
|
18
fastlane/metadata/android/pt/full_description.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
Uma excelente forma para manter o contacto com os seus amigos e familiares. Também pode ser utilizada para mensagens de grupo e possibilita bloqueio de números de telefone em versões Android 7+.
|
||||||
|
|
||||||
|
Permite a utilização de vários formatos de data. Também pode alternar o formato das horas.
|
||||||
|
|
||||||
|
É uma aplicação pequena - comparada com as aplicações do mesmo género - o que torna a sua descarga muito rápida.
|
||||||
|
|
||||||
|
Disponibiliza um design atrativo e um tema escuro por omissão. A não utilização da permissão Internet providencia-lhe mais privacidade, segurança e estabilidade do que as outras aplicações.
|
||||||
|
|
||||||
|
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>
|
||||||
|
https://www.simplemobiletools.com
|
||||||
|
|
||||||
|
<b>Facebook:</b>
|
||||||
|
https://www.facebook.com/simplemobiletools
|
||||||
|
|
||||||
|
<b>Reddit:</b>
|
||||||
|
https://www.reddit.com/r/SimpleMobileTools
|
1
fastlane/metadata/android/pt/short_description.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Aplicação simples para gerir SMS e MMS, sem anúncios.
|
1
fastlane/metadata/android/pt/title.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Simple SMS Messenger - Gestão de mensagens
|
18
fastlane/metadata/android/ru/full_description.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
SMS/MMS-сообщения — это отличный способ оставаться на связи со своими родственниками. Приложение также правильно обрабатывает групповые сообщения и блокировку номеров на Android 7+.
|
||||||
|
|
||||||
|
Поддерживается множество вариантов отображения даты, чтобы вам было удобно пользоваться приложением. Также вы можете переключаться между 12- и 24-часовым форматом времени.
|
||||||
|
|
||||||
|
Наше приложение имеет очень маленький размер по сравнению с конкурентами, что делает его загрузку такой быстрой.
|
||||||
|
|
||||||
|
Приложение выпускается с материальным дизайном и тёмной темой по умолчанию, что обеспечивает удобное использование. Отсутствие доступа в интернет обеспечивает вам больше приватности, безопасности и стабильности, чем в других приложениях.
|
||||||
|
|
||||||
|
Не содержит рекламы или ненужных разрешений. Полностью открытый исходный код, есть возможность настроить цвета интерфейса.
|
||||||
|
|
||||||
|
<b>Ознакомьтесь с полным набором инструментов серии Simple здесь:</b>
|
||||||
|
https://www.simplemobiletools.com
|
||||||
|
|
||||||
|
<b>Facebook:</b>
|
||||||
|
https://www.facebook.com/simplemobiletools
|
||||||
|
|
||||||
|
<b>Reddit:</b>
|
||||||
|
https://www.reddit.com/r/SimpleMobileTools
|
1
fastlane/metadata/android/ru/short_description.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Простой и быстрый способ управления сообщениями SMS и MMS без рекламы.
|
1
fastlane/metadata/android/ru/title.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Simple SMS Messenger - лёгкое управление SMS
|