Merge branch 'master' of https://github.com/SimpleMobileTools/Simple-SMS-Messenger
# Conflicts: # gradle/wrapper/gradle-wrapper.properties
|
@ -17,7 +17,7 @@ insert_final_newline = true
|
|||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
continuation_indent_size = 8
|
||||
continuation_indent_size = 4
|
||||
|
||||
[*.xml]
|
||||
continuation_indent_size = 4
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
github: [tibbi]
|
||||
patreon: tiborkaputa
|
||||
custom: ["https://www.paypal.com/paypalme/simplemobiletools", "https://www.simplemobiletools.com/donate"]
|
78
CHANGELOG.md
|
@ -1,6 +1,84 @@
|
|||
Changelog
|
||||
==========
|
||||
|
||||
Version 5.9.0 *(2021-02-16)*
|
||||
----------------------------
|
||||
|
||||
* Added Search
|
||||
* Added a White theme with special handling
|
||||
* Some stability and translation improvements
|
||||
|
||||
Version 5.8.3 *(2021-01-27)*
|
||||
----------------------------
|
||||
|
||||
* Adding some stability and translation improvements
|
||||
|
||||
Version 5.8.2 *(2021-01-18)*
|
||||
----------------------------
|
||||
|
||||
* Fixed a glitch with inability to send messages in empty conversations
|
||||
* Adding a settings item for quickly getting into notification settings
|
||||
* Some stability and translation improvements
|
||||
|
||||
Version 5.8.1 *(2021-01-11)*
|
||||
----------------------------
|
||||
|
||||
* Fixed a glitch with "Sending..." stuck at messages
|
||||
* Allow selecting a phone numbers at contacts with multiple ones
|
||||
* Some translation and stability improvements
|
||||
|
||||
Version 5.8.0 *(2021-01-02)*
|
||||
----------------------------
|
||||
|
||||
* Improved delivery reports, show a notification at failed messages
|
||||
* Allow resending failed messages with tapping them
|
||||
* Added multiple dual-SIM related improvements
|
||||
* Many other UX, stability, performance and translation improvements
|
||||
|
||||
Version 5.7.0 *(2020-12-30)*
|
||||
----------------------------
|
||||
|
||||
* Prefetch all messages at first launch for better performance
|
||||
* Require Simple Thank You for color customization
|
||||
* Some stability, translation and UX improvements
|
||||
|
||||
Version 5.6.2 *(2020-12-25)*
|
||||
----------------------------
|
||||
|
||||
* Fixed messages not being sent in some cases
|
||||
|
||||
Version 5.6.1 *(2020-12-24)*
|
||||
----------------------------
|
||||
|
||||
* Fixing a crash at devices with multiple SIM cards
|
||||
|
||||
Version 5.6.0 *(2020-12-23)*
|
||||
----------------------------
|
||||
|
||||
* Cache messages for better performance
|
||||
* Add a scrollbar on the main screen
|
||||
* Adding some stability and translation improvements
|
||||
|
||||
Version 5.5.1 *(2020-12-06)*
|
||||
----------------------------
|
||||
|
||||
* Properly show private contact names at group conversations
|
||||
* Fixed private contacts not being visible on Android 11+
|
||||
* Some stability and translation improvements
|
||||
|
||||
Version 5.5.0 *(2020-11-04)*
|
||||
----------------------------
|
||||
|
||||
* Allow dialing or copying selected conversation phone numbers
|
||||
* Allow copying specific parts of messages into clipboard
|
||||
* Adding an option to show character counter at outgoing messages
|
||||
* Couple other UI, translation and stability improvements
|
||||
|
||||
Version 5.4.5 *(2020-10-27)*
|
||||
----------------------------
|
||||
|
||||
* Fixed some smaller glitches + translation improvements
|
||||
|
||||
Version 5.4.4 *(2020-09-23)*
|
||||
----------------------------
|
||||
|
||||
|
|
11
README.md
|
@ -11,20 +11,21 @@ It comes with material design and dark theme by default, provides great user exp
|
|||
|
||||
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
|
||||
|
||||
Check out the full suite of Simple Tools here:
|
||||
Check out the full suite of Simple Tools here:
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
Facebook:
|
||||
Facebook:
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
Reddit:
|
||||
Reddit:
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
|
||||
<a href='https://play.google.com/store/apps/details?id=com.simplemobiletools.smsmessenger'><img src='https://simplemobiletools.com/assets/images/google-play.png' alt='Get it on Google Play' height=45/></a>
|
||||
<a href='https://f-droid.org/packages/com.simplemobiletools.smsmessenger'><img src='https://simplemobiletools.com/assets/images/f-droid.png' alt='Get it on F-Droid' height='45' /></a>
|
||||
|
||||
<div style="display:flex;">
|
||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_1.jpg" width="30%">
|
||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg" width="30%">
|
||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/english/1.jpg" width="30%">
|
||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/english/2.jpg" width="30%">
|
||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/english/3.jpg" width="30%">
|
||||
</div>
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ android {
|
|||
applicationId "com.simplemobiletools.smsmessenger"
|
||||
minSdkVersion 22
|
||||
targetSdkVersion 30
|
||||
versionCode 16
|
||||
versionName "5.4.4"
|
||||
versionCode 29
|
||||
versionName "5.9.0"
|
||||
setProperty("archivesBaseName", "sms-messenger")
|
||||
}
|
||||
|
||||
|
@ -56,13 +56,14 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.simplemobiletools:commons:5.30.12'
|
||||
implementation 'com.simplemobiletools:commons:5.33.32'
|
||||
implementation 'org.greenrobot:eventbus:3.2.0'
|
||||
implementation 'com.klinkerapps:android-smsmms:5.2.6'
|
||||
implementation 'com.github.tibbi:IndicatorFastScroll:08f512858a'
|
||||
implementation 'com.github.tibbi:IndicatorFastScroll:c3de1d040a'
|
||||
implementation "me.leolin:ShortcutBadger:1.1.22"
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
|
||||
kapt "androidx.room:room-compiler:2.2.5"
|
||||
implementation "androidx.room:room-runtime:2.2.5"
|
||||
annotationProcessor "androidx.room:room-compiler:2.2.5"
|
||||
kapt "androidx.room:room-compiler:2.2.6"
|
||||
implementation "androidx.room:room-runtime:2.2.6"
|
||||
annotationProcessor "androidx.room:room-compiler:2.2.6"
|
||||
}
|
||||
|
|
|
@ -38,6 +38,21 @@
|
|||
android:name=".activities.ThreadActivity"
|
||||
android:parentActivityName=".activities.MainActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.SearchActivity"
|
||||
android:label=""
|
||||
android:parentActivityName=".activities.MainActivity"
|
||||
android:resizeableActivity="true">
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:resource="@xml/searchable"/>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activities.NewConversationActivity"
|
||||
android:parentActivityName=".activities.MainActivity">
|
||||
|
@ -121,6 +136,10 @@
|
|||
|
||||
<service android:name="com.android.mms.transaction.TransactionService" />
|
||||
|
||||
<receiver android:name=".receivers.SmsStatusSentReceiver" />
|
||||
|
||||
<receiver android:name=".receivers.SmsStatusDeliveredReceiver" />
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.SmsReceiver"
|
||||
android:permission="android.permission.BROADCAST_SMS">
|
||||
|
@ -149,11 +168,6 @@
|
|||
android:exported="true"
|
||||
android:taskAffinity="com.klinker.android.messaging.MMS_RECEIVED" />
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.SmsSentReceiver"
|
||||
android:exported="true"
|
||||
android:taskAffinity="${applicationId}.SMS_SENT" />
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.MarkAsReadReceiver"
|
||||
android:enabled="true"
|
||||
|
|
|
@ -18,10 +18,7 @@ import com.simplemobiletools.commons.models.FAQItem
|
|||
import com.simplemobiletools.smsmessenger.BuildConfig
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.adapters.ConversationsAdapter
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import com.simplemobiletools.smsmessenger.extensions.conversationsDB
|
||||
import com.simplemobiletools.smsmessenger.extensions.getConversations
|
||||
import com.simplemobiletools.smsmessenger.extensions.updateUnreadCountBadge
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
|
||||
import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
|
@ -86,6 +83,8 @@ class MainActivity : SimpleActivity() {
|
|||
updateTextColors(main_coordinator)
|
||||
no_conversations_placeholder_2.setTextColor(getAdjustedPrimaryColor())
|
||||
no_conversations_placeholder_2.underlineText()
|
||||
conversations_fastscroller.updatePrimaryColor()
|
||||
conversations_fastscroller.updateBubbleColors()
|
||||
checkShortcut()
|
||||
}
|
||||
|
||||
|
@ -101,11 +100,13 @@ class MainActivity : SimpleActivity() {
|
|||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
updateMenuItemColors(menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.search -> launchSearch()
|
||||
R.id.settings -> launchSettings()
|
||||
R.id.about -> launchAbout()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
|
@ -138,7 +139,10 @@ class MainActivity : SimpleActivity() {
|
|||
handlePermission(PERMISSION_READ_CONTACTS) {
|
||||
initMessenger()
|
||||
bus = EventBus.getDefault()
|
||||
bus!!.register(this)
|
||||
try {
|
||||
bus!!.register(this)
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
finish()
|
||||
|
@ -165,7 +169,12 @@ class MainActivity : SimpleActivity() {
|
|||
|
||||
private fun getCachedConversations() {
|
||||
ensureBackgroundThread {
|
||||
val conversations = conversationsDB.getAll().sortedByDescending { it.date }.toMutableList() as ArrayList<Conversation>
|
||||
val conversations = try {
|
||||
conversationsDB.getAll().sortedByDescending { it.date }.toMutableList() as ArrayList<Conversation>
|
||||
} catch (e: Exception) {
|
||||
ArrayList()
|
||||
}
|
||||
|
||||
updateUnreadCountBadge(conversations)
|
||||
runOnUiThread {
|
||||
setupConversations(conversations)
|
||||
|
@ -175,46 +184,43 @@ class MainActivity : SimpleActivity() {
|
|||
}
|
||||
|
||||
private fun getNewConversations(cachedConversations: ArrayList<Conversation>) {
|
||||
val privateCursor = getMyContactsCursor().loadInBackground()
|
||||
val privateCursor = getMyContactsCursor()?.loadInBackground()
|
||||
ensureBackgroundThread {
|
||||
val conversations = getConversations()
|
||||
|
||||
// check if no message came from a privately stored contact in Simple Contacts
|
||||
val privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
if (privateContacts.isNotEmpty()) {
|
||||
conversations.filter { it.title == it.phoneNumber }.forEach { conversation ->
|
||||
privateContacts.forEach { contact ->
|
||||
if (contact.doesContainPhoneNumber(conversation.phoneNumber)) {
|
||||
conversation.title = contact.name
|
||||
conversation.photoUri = contact.photoUri
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val conversations = getConversations(privateContacts = privateContacts)
|
||||
|
||||
runOnUiThread {
|
||||
setupConversations(conversations)
|
||||
}
|
||||
|
||||
conversations.forEach { clonedConversation ->
|
||||
if (!cachedConversations.map { it.thread_id }.contains(clonedConversation.thread_id)) {
|
||||
if (!cachedConversations.map { it.threadId }.contains(clonedConversation.threadId)) {
|
||||
conversationsDB.insertOrUpdate(clonedConversation)
|
||||
cachedConversations.add(clonedConversation)
|
||||
}
|
||||
}
|
||||
|
||||
cachedConversations.forEach { cachedConversation ->
|
||||
if (!conversations.map { it.thread_id }.contains(cachedConversation.thread_id)) {
|
||||
conversationsDB.delete(cachedConversation.id!!)
|
||||
if (!conversations.map { it.threadId }.contains(cachedConversation.threadId)) {
|
||||
conversationsDB.deleteThreadId(cachedConversation.threadId)
|
||||
}
|
||||
}
|
||||
|
||||
cachedConversations.forEach { cachedConversation ->
|
||||
val conv = conversations.firstOrNull { it.thread_id == cachedConversation.thread_id && it.getStringToCompare() != cachedConversation.getStringToCompare() }
|
||||
val conv = conversations.firstOrNull { it.threadId == cachedConversation.threadId && it.toString() != cachedConversation.toString() }
|
||||
if (conv != null) {
|
||||
conversationsDB.insertOrUpdate(conv)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.appRunCount == 1) {
|
||||
conversations.map { it.threadId }.forEach { threadId ->
|
||||
val messages = getMessages(threadId)
|
||||
messages.chunked(30).forEach { currentMessages ->
|
||||
messagesDB.insertMessages(*currentMessages.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,17 +230,27 @@ class MainActivity : SimpleActivity() {
|
|||
no_conversations_placeholder.beVisibleIf(!hasConversations)
|
||||
no_conversations_placeholder_2.beVisibleIf(!hasConversations)
|
||||
|
||||
if (!hasConversations && config.appRunCount == 1) {
|
||||
no_conversations_placeholder.text = getString(R.string.loading_messages)
|
||||
no_conversations_placeholder_2.beGone()
|
||||
}
|
||||
|
||||
val currAdapter = conversations_list.adapter
|
||||
if (currAdapter == null) {
|
||||
ConversationsAdapter(this, conversations, conversations_list, conversations_fastscroller) {
|
||||
Intent(this, ThreadActivity::class.java).apply {
|
||||
putExtra(THREAD_ID, (it as Conversation).thread_id)
|
||||
putExtra(THREAD_ID, (it as Conversation).threadId)
|
||||
putExtra(THREAD_TITLE, it.title)
|
||||
startActivity(this)
|
||||
}
|
||||
}.apply {
|
||||
conversations_list.adapter = this
|
||||
}
|
||||
|
||||
conversations_fastscroller.setViews(conversations_list) {
|
||||
val listItem = (conversations_list.adapter as? ConversationsAdapter)?.conversations?.getOrNull(it)
|
||||
conversations_fastscroller.updateBubbleText(listItem?.title ?: "")
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
(currAdapter as ConversationsAdapter).updateConversations(conversations)
|
||||
|
@ -281,6 +297,10 @@ class MainActivity : SimpleActivity() {
|
|||
.build()
|
||||
}
|
||||
|
||||
private fun launchSearch() {
|
||||
startActivity(Intent(applicationContext, SearchActivity::class.java))
|
||||
}
|
||||
|
||||
private fun launchSettings() {
|
||||
startActivity(Intent(applicationContext, SettingsActivity::class.java))
|
||||
}
|
||||
|
@ -290,7 +310,8 @@ class MainActivity : SimpleActivity() {
|
|||
|
||||
val faqItems = arrayListOf(
|
||||
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons),
|
||||
FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons)
|
||||
FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons),
|
||||
FAQItem(R.string.faq_9_title_commons, R.string.faq_9_text_commons)
|
||||
)
|
||||
|
||||
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
|
||||
|
|
|
@ -5,12 +5,12 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.WindowManager
|
||||
import com.google.gson.Gson
|
||||
import com.reddit.indicatorfastscroll.FastScrollItemIndicator
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.MyContactsContentProvider
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
|
||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.commons.models.SimpleContact
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.adapters.ContactsAdapter
|
||||
|
@ -89,9 +89,12 @@ class NewConversationActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
val adjustedPrimaryColor = getAdjustedPrimaryColor()
|
||||
contacts_letter_fastscroller.textColor = config.textColor.getColorStateList()
|
||||
contacts_letter_fastscroller.pressedTextColor = adjustedPrimaryColor
|
||||
contacts_letter_fastscroller_thumb.setupWithFastScroller(contacts_letter_fastscroller)
|
||||
contacts_letter_fastscroller_thumb.textColor = config.primaryColor.getContrastColor()
|
||||
contacts_letter_fastscroller_thumb?.textColor = adjustedPrimaryColor.getContrastColor()
|
||||
contacts_letter_fastscroller_thumb?.thumbColor = adjustedPrimaryColor.getColorStateList()
|
||||
}
|
||||
|
||||
private fun isThirdPartyIntent(): Boolean {
|
||||
|
@ -134,7 +137,20 @@ class NewConversationActivity : SimpleActivity() {
|
|||
|
||||
ContactsAdapter(this, contacts, contacts_list, null) {
|
||||
hideKeyboard()
|
||||
launchThreadActivity((it as SimpleContact).phoneNumbers.first(), it.name)
|
||||
val contact = it as SimpleContact
|
||||
val phoneNumbers = contact.phoneNumbers
|
||||
if (phoneNumbers.size > 1) {
|
||||
val items = ArrayList<RadioItem>()
|
||||
phoneNumbers.forEachIndexed { index, phoneNumber ->
|
||||
items.add(RadioItem(index, phoneNumber, phoneNumber))
|
||||
}
|
||||
|
||||
RadioGroupDialog(this, items) {
|
||||
launchThreadActivity(it as String, contact.name)
|
||||
}
|
||||
} else {
|
||||
launchThreadActivity(phoneNumbers.first(), contact.name)
|
||||
}
|
||||
}.apply {
|
||||
contacts_list.adapter = this
|
||||
}
|
||||
|
@ -143,7 +159,7 @@ class NewConversationActivity : SimpleActivity() {
|
|||
}
|
||||
|
||||
private fun fillSuggestedContacts(callback: () -> Unit) {
|
||||
val privateCursor = getMyContactsCursor().loadInBackground()
|
||||
val privateCursor = getMyContactsCursor()?.loadInBackground()
|
||||
ensureBackgroundThread {
|
||||
privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
val suggestions = getSuggestedContacts(privateContacts)
|
||||
|
@ -160,10 +176,13 @@ class NewConversationActivity : SimpleActivity() {
|
|||
layoutInflater.inflate(R.layout.item_suggested_contact, null).apply {
|
||||
suggested_contact_name.text = contact.name
|
||||
suggested_contact_name.setTextColor(baseConfig.textColor)
|
||||
SimpleContactsHelper(this@NewConversationActivity).loadContactImage(contact.photoUri, suggested_contact_image, contact.name)
|
||||
suggestions_holder.addView(this)
|
||||
setOnClickListener {
|
||||
launchThreadActivity(contact.phoneNumbers.first(), contact.name)
|
||||
|
||||
if (!isDestroyed) {
|
||||
SimpleContactsHelper(this@NewConversationActivity).loadContactImage(contact.photoUri, suggested_contact_image, contact.name)
|
||||
suggestions_holder.addView(this)
|
||||
setOnClickListener {
|
||||
launchThreadActivity(contact.phoneNumbers.first(), contact.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,11 +206,13 @@ class NewConversationActivity : SimpleActivity() {
|
|||
|
||||
private fun launchThreadActivity(phoneNumber: String, name: String) {
|
||||
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
|
||||
val numbers = phoneNumber.split(";").toSet()
|
||||
val number = if (numbers.size == 1) phoneNumber else Gson().toJson(numbers)
|
||||
Intent(this, ThreadActivity::class.java).apply {
|
||||
putExtra(THREAD_ID, getThreadId(phoneNumber).toInt())
|
||||
putExtra(THREAD_ID, getThreadId(numbers))
|
||||
putExtra(THREAD_TITLE, name)
|
||||
putExtra(THREAD_TEXT, text)
|
||||
putExtra(THREAD_NUMBER, phoneNumber)
|
||||
putExtra(THREAD_NUMBER, number)
|
||||
|
||||
if (intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
|
||||
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package com.simplemobiletools.smsmessenger.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.SearchManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.MenuItemCompat
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.adapters.SearchResultsAdapter
|
||||
import com.simplemobiletools.smsmessenger.extensions.conversationsDB
|
||||
import com.simplemobiletools.smsmessenger.extensions.messagesDB
|
||||
import com.simplemobiletools.smsmessenger.helpers.SEARCHED_MESSAGE_ID
|
||||
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
|
||||
import com.simplemobiletools.smsmessenger.helpers.THREAD_TITLE
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
import com.simplemobiletools.smsmessenger.models.Message
|
||||
import com.simplemobiletools.smsmessenger.models.SearchResult
|
||||
import kotlinx.android.synthetic.main.activity_search.*
|
||||
|
||||
class SearchActivity : SimpleActivity() {
|
||||
private var mIsSearchOpen = false
|
||||
private var mLastSearchedText = ""
|
||||
private var mSearchMenuItem: MenuItem? = null
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_search)
|
||||
updateTextColors(search_holder)
|
||||
search_placeholder.setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize())
|
||||
search_placeholder_2.setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize())
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_search, menu)
|
||||
setupSearch(menu)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setupSearch(menu: Menu) {
|
||||
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
|
||||
mSearchMenuItem = menu.findItem(R.id.search)
|
||||
|
||||
MenuItemCompat.setOnActionExpandListener(mSearchMenuItem, object : MenuItemCompat.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
mIsSearchOpen = true
|
||||
return true
|
||||
}
|
||||
|
||||
// this triggers on device rotation too, avoid doing anything
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
if (mIsSearchOpen) {
|
||||
mIsSearchOpen = false
|
||||
mLastSearchedText = ""
|
||||
finish()
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
mSearchMenuItem?.expandActionView()
|
||||
(mSearchMenuItem?.actionView as? SearchView)?.apply {
|
||||
setSearchableInfo(searchManager.getSearchableInfo(componentName))
|
||||
isSubmitButtonEnabled = false
|
||||
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String) = false
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
if (mIsSearchOpen) {
|
||||
mLastSearchedText = newText
|
||||
textChanged(newText)
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun textChanged(text: String) {
|
||||
search_placeholder_2.beGoneIf(text.length >= 2)
|
||||
if (text.length >= 2) {
|
||||
ensureBackgroundThread {
|
||||
val searchQuery = "%$text%"
|
||||
val messages = messagesDB.getMessagesWithText(searchQuery)
|
||||
val conversations = conversationsDB.getConversationsWithText(searchQuery)
|
||||
if (text == mLastSearchedText) {
|
||||
showSearchResults(messages, conversations, text)
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
search_placeholder.beVisible()
|
||||
search_results_list.beGone()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSearchResults(messages: List<Message>, conversations: List<Conversation>, searchedText: String) {
|
||||
val searchResults = ArrayList<SearchResult>()
|
||||
conversations.forEach { conversation ->
|
||||
val date = conversation.date.formatDateOrTime(this, true, true)
|
||||
val searchResult = SearchResult(-1, conversation.title, conversation.phoneNumber, date, conversation.threadId, conversation.photoUri)
|
||||
searchResults.add(searchResult)
|
||||
}
|
||||
|
||||
messages.forEach { message ->
|
||||
var recipient = message.senderName
|
||||
if (recipient.isEmpty() && message.participants.isNotEmpty()) {
|
||||
val participantNames = message.participants.map { it.name }
|
||||
recipient = TextUtils.join(", ", participantNames)
|
||||
}
|
||||
|
||||
val date = message.date.formatDateOrTime(this, true, true)
|
||||
val searchResult = SearchResult(message.id, recipient, message.body, date, message.threadId, message.senderPhotoUri)
|
||||
searchResults.add(searchResult)
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
search_results_list.beVisibleIf(searchResults.isNotEmpty())
|
||||
search_placeholder.beVisibleIf(searchResults.isEmpty())
|
||||
|
||||
val currAdapter = search_results_list.adapter
|
||||
if (currAdapter == null) {
|
||||
SearchResultsAdapter(this, searchResults, search_results_list, searchedText) {
|
||||
Intent(this, ThreadActivity::class.java).apply {
|
||||
putExtra(THREAD_ID, (it as SearchResult).threadId)
|
||||
putExtra(THREAD_TITLE, it.title)
|
||||
putExtra(SEARCHED_MESSAGE_ID, it.messageId)
|
||||
startActivity(this)
|
||||
}
|
||||
}.apply {
|
||||
search_results_list.adapter = this
|
||||
}
|
||||
} else {
|
||||
(currAdapter as SearchResultsAdapter).updateItems(searchResults, searchedText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,10 +30,12 @@ class SettingsActivity : SimpleActivity() {
|
|||
|
||||
setupPurchaseThankYou()
|
||||
setupCustomizeColors()
|
||||
setupCustomizeNotifications()
|
||||
setupUseEnglish()
|
||||
setupManageBlockedNumbers()
|
||||
setupChangeDateTimeFormat()
|
||||
setupFontSize()
|
||||
setupShowCharacterCounter()
|
||||
updateTextColors(settings_scrollview)
|
||||
|
||||
if (blockedNumbersAtPause != -1 && blockedNumbersAtPause != getBlockedNumbers().hashCode()) {
|
||||
|
@ -52,15 +54,23 @@ class SettingsActivity : SimpleActivity() {
|
|||
}
|
||||
|
||||
private fun setupPurchaseThankYou() {
|
||||
settings_purchase_thank_you_holder.beVisibleIf(!isThankYouInstalled())
|
||||
settings_purchase_thank_you_holder.beGoneIf(isOrWasThankYouInstalled())
|
||||
settings_purchase_thank_you_holder.setOnClickListener {
|
||||
launchPurchaseThankYouIntent()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCustomizeColors() {
|
||||
settings_customize_colors_label.text = getCustomizeColorsString()
|
||||
settings_customize_colors_holder.setOnClickListener {
|
||||
startCustomizationActivity()
|
||||
handleCustomizeColorsClick()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCustomizeNotifications() {
|
||||
settings_customize_notifications_holder.beVisibleIf(isOreoPlus())
|
||||
settings_customize_notifications_holder.setOnClickListener {
|
||||
launchCustomizeNotificationsIntent()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,4 +116,12 @@ class SettingsActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupShowCharacterCounter() {
|
||||
settings_show_character_counter.isChecked = config.showCharacterCounter
|
||||
settings_show_character_counter_holder.setOnClickListener {
|
||||
settings_show_character_counter.toggle()
|
||||
config.showCharacterCounter = settings_show_character_counter.isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,15 @@ import android.app.Activity
|
|||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.provider.Telephony
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.text.TextUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.*
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.LinearLayout
|
||||
|
@ -38,6 +41,9 @@ import com.simplemobiletools.smsmessenger.adapters.ThreadAdapter
|
|||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import com.simplemobiletools.smsmessenger.receivers.SmsStatusDeliveredReceiver
|
||||
import com.simplemobiletools.smsmessenger.receivers.SmsStatusSentReceiver
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.activity_thread.*
|
||||
import kotlinx.android.synthetic.main.item_attachment.view.*
|
||||
import kotlinx.android.synthetic.main.item_selected_contact.view.*
|
||||
|
@ -49,9 +55,10 @@ class ThreadActivity : SimpleActivity() {
|
|||
private val MIN_DATE_TIME_DIFF_SECS = 300
|
||||
private val PICK_ATTACHMENT_INTENT = 1
|
||||
|
||||
private var threadId = 0
|
||||
private var threadId = 0L
|
||||
private var currentSIMCardIndex = 0
|
||||
private var isActivityVisible = false
|
||||
private var refreshedSinceSent = false
|
||||
private var threadItems = ArrayList<ThreadItem>()
|
||||
private var bus: EventBus? = null
|
||||
private var participants = ArrayList<SimpleContact>()
|
||||
|
@ -71,7 +78,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
return
|
||||
}
|
||||
|
||||
threadId = intent.getIntExtra(THREAD_ID, 0)
|
||||
threadId = intent.getLongExtra(THREAD_ID, 0L)
|
||||
intent.getStringExtra(THREAD_TITLE)?.let {
|
||||
supportActionBar?.title = it
|
||||
}
|
||||
|
@ -80,7 +87,19 @@ class ThreadActivity : SimpleActivity() {
|
|||
bus!!.register(this)
|
||||
handlePermission(PERMISSION_READ_PHONE_STATE) {
|
||||
if (it) {
|
||||
setupThread()
|
||||
setupButtons()
|
||||
setupCachedMessages {
|
||||
val searchedMessageId = intent.getLongExtra(SEARCHED_MESSAGE_ID, -1L)
|
||||
intent.removeExtra(SEARCHED_MESSAGE_ID)
|
||||
if (searchedMessageId != -1L) {
|
||||
val index = threadItems.indexOfFirst { (it as? Message)?.id == searchedMessageId }
|
||||
if (index != -1) {
|
||||
thread_messages_list.smoothScrollToPosition(index)
|
||||
}
|
||||
}
|
||||
|
||||
setupThread()
|
||||
}
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
|
@ -97,94 +116,6 @@ class ThreadActivity : SimpleActivity() {
|
|||
isActivityVisible = false
|
||||
}
|
||||
|
||||
private fun setupThread() {
|
||||
val privateCursor = getMyContactsCursor().loadInBackground()
|
||||
ensureBackgroundThread {
|
||||
messages = getMessages(threadId)
|
||||
participants = if (messages.isEmpty()) {
|
||||
getThreadParticipants(threadId, null)
|
||||
} else {
|
||||
messages.first().participants
|
||||
}
|
||||
|
||||
// check if no participant came from a privately stored contact in Simple Contacts
|
||||
privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
if (privateContacts.isNotEmpty()) {
|
||||
val senderNumbersToReplace = HashMap<String, String>()
|
||||
participants.filter { it.doesContainPhoneNumber(it.name) }.forEach { participant ->
|
||||
privateContacts.firstOrNull { it.doesContainPhoneNumber(participant.phoneNumbers.first()) }?.apply {
|
||||
senderNumbersToReplace[participant.phoneNumbers.first()] = name
|
||||
participant.name = name
|
||||
participant.photoUri = photoUri
|
||||
}
|
||||
}
|
||||
|
||||
messages.forEach { message ->
|
||||
if (senderNumbersToReplace.keys.contains(message.senderName)) {
|
||||
message.senderName = senderNumbersToReplace[message.senderName]!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (participants.isEmpty()) {
|
||||
val name = intent.getStringExtra(THREAD_TITLE) ?: ""
|
||||
val number = intent.getStringExtra(THREAD_NUMBER)
|
||||
if (number == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
finish()
|
||||
return@ensureBackgroundThread
|
||||
}
|
||||
|
||||
val contact = SimpleContact(0, 0, name, "", arrayListOf(number))
|
||||
participants.add(contact)
|
||||
}
|
||||
|
||||
messages.filter { it.attachment != null }.forEach {
|
||||
it.attachment!!.attachments.forEach {
|
||||
try {
|
||||
if (it.mimetype.startsWith("image/")) {
|
||||
val fileOptions = BitmapFactory.Options()
|
||||
fileOptions.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeStream(contentResolver.openInputStream(it.uri), null, fileOptions)
|
||||
it.width = fileOptions.outWidth
|
||||
it.height = fileOptions.outHeight
|
||||
} else if (it.mimetype.startsWith("video/")) {
|
||||
val metaRetriever = MediaMetadataRetriever()
|
||||
metaRetriever.setDataSource(this, it.uri)
|
||||
it.width = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt()
|
||||
it.height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
|
||||
}
|
||||
|
||||
if (it.width < 0) {
|
||||
it.width = 0
|
||||
}
|
||||
|
||||
if (it.height < 0) {
|
||||
it.height = 0
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupAdapter()
|
||||
runOnUiThread {
|
||||
val threadTitle = participants.getThreadTitle()
|
||||
if (threadTitle.isNotEmpty()) {
|
||||
supportActionBar?.title = participants.getThreadTitle()
|
||||
}
|
||||
|
||||
if (messages.isEmpty()) {
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
thread_type_message.requestFocus()
|
||||
}
|
||||
|
||||
setupSIMSelector()
|
||||
}
|
||||
}
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
bus?.unregister(this)
|
||||
|
@ -223,13 +154,98 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupCachedMessages(callback: () -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
messages = messagesDB.getThreadMessages(threadId).toMutableList() as ArrayList<Message>
|
||||
setupParticipants()
|
||||
setupAdapter()
|
||||
|
||||
runOnUiThread {
|
||||
if (messages.isEmpty()) {
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
thread_type_message.requestFocus()
|
||||
}
|
||||
|
||||
setupThreadTitle()
|
||||
setupSIMSelector()
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupThread() {
|
||||
val privateCursor = getMyContactsCursor()?.loadInBackground()
|
||||
ensureBackgroundThread {
|
||||
val cachedMessagesCode = messages.hashCode()
|
||||
messages = getMessages(threadId)
|
||||
if (messages.hashCode() == cachedMessagesCode && participants.isNotEmpty()) {
|
||||
return@ensureBackgroundThread
|
||||
}
|
||||
|
||||
setupParticipants()
|
||||
|
||||
// check if no participant came from a privately stored contact in Simple Contacts
|
||||
privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
if (privateContacts.isNotEmpty()) {
|
||||
val senderNumbersToReplace = HashMap<String, String>()
|
||||
participants.filter { it.doesContainPhoneNumber(it.name) }.forEach { participant ->
|
||||
privateContacts.firstOrNull { it.doesContainPhoneNumber(participant.phoneNumbers.first()) }?.apply {
|
||||
senderNumbersToReplace[participant.phoneNumbers.first()] = name
|
||||
participant.name = name
|
||||
participant.photoUri = photoUri
|
||||
}
|
||||
}
|
||||
|
||||
messages.forEach { message ->
|
||||
if (senderNumbersToReplace.keys.contains(message.senderName)) {
|
||||
message.senderName = senderNumbersToReplace[message.senderName]!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (participants.isEmpty()) {
|
||||
val name = intent.getStringExtra(THREAD_TITLE) ?: ""
|
||||
val number = intent.getStringExtra(THREAD_NUMBER)
|
||||
if (number == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
finish()
|
||||
return@ensureBackgroundThread
|
||||
}
|
||||
|
||||
val contact = SimpleContact(0, 0, name, "", arrayListOf(number), ArrayList(), ArrayList())
|
||||
participants.add(contact)
|
||||
}
|
||||
|
||||
messages.chunked(30).forEach { currentMessages ->
|
||||
messagesDB.insertMessages(*currentMessages.toTypedArray())
|
||||
}
|
||||
|
||||
setupAttachmentSizes()
|
||||
setupAdapter()
|
||||
runOnUiThread {
|
||||
setupThreadTitle()
|
||||
setupSIMSelector()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAdapter() {
|
||||
threadItems = getThreadItems()
|
||||
invalidateOptionsMenu()
|
||||
|
||||
runOnUiThread {
|
||||
val adapter = ThreadAdapter(this, threadItems, thread_messages_list, thread_messages_fastscroller) {}
|
||||
thread_messages_list.adapter = adapter
|
||||
val currAdapter = thread_messages_list.adapter
|
||||
if (currAdapter == null) {
|
||||
ThreadAdapter(this, threadItems, thread_messages_list, thread_messages_fastscroller) {
|
||||
(it as? ThreadError)?.apply {
|
||||
thread_type_message.setText(it.messageText)
|
||||
}
|
||||
}.apply {
|
||||
thread_messages_list.adapter = this
|
||||
}
|
||||
} else {
|
||||
(currAdapter as ThreadAdapter).updateMessages(threadItems)
|
||||
}
|
||||
}
|
||||
|
||||
SimpleContactsHelper(this).getAvailableContacts(false) { contacts ->
|
||||
|
@ -252,7 +268,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
|
||||
confirm_inserted_number?.setOnClickListener {
|
||||
val number = add_contact_or_number.value
|
||||
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", arrayListOf(number))
|
||||
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", arrayListOf(number), ArrayList(), ArrayList())
|
||||
addSelectedContact(contact)
|
||||
}
|
||||
}
|
||||
|
@ -264,6 +280,10 @@ class ThreadActivity : SimpleActivity() {
|
|||
confirm_manage_contacts.applyColorFilter(textColor)
|
||||
thread_add_attachment.applyColorFilter(textColor)
|
||||
|
||||
thread_character_counter.beVisibleIf(config.showCharacterCounter)
|
||||
thread_character_counter.setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize())
|
||||
|
||||
thread_type_message.setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize())
|
||||
thread_send_message.setOnClickListener {
|
||||
sendMessage()
|
||||
}
|
||||
|
@ -271,6 +291,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
thread_send_message.isClickable = false
|
||||
thread_type_message.onTextChangeListener {
|
||||
checkSendMessageAvailability()
|
||||
thread_character_counter.text = it.length.toString()
|
||||
}
|
||||
|
||||
confirm_manage_contacts.setOnClickListener {
|
||||
|
@ -284,7 +305,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
val newThreadId = getThreadId(numbers).toInt()
|
||||
val newThreadId = getThreadId(numbers)
|
||||
if (threadId != newThreadId) {
|
||||
Intent(this, ThreadActivity::class.java).apply {
|
||||
putExtra(THREAD_ID, newThreadId)
|
||||
|
@ -309,12 +330,59 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupAttachmentSizes() {
|
||||
messages.filter { it.attachment != null }.forEach {
|
||||
it.attachment!!.attachments.forEach {
|
||||
try {
|
||||
if (it.mimetype.startsWith("image/")) {
|
||||
val fileOptions = BitmapFactory.Options()
|
||||
fileOptions.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeStream(contentResolver.openInputStream(it.getUri()), null, fileOptions)
|
||||
it.width = fileOptions.outWidth
|
||||
it.height = fileOptions.outHeight
|
||||
} else if (it.mimetype.startsWith("video/")) {
|
||||
val metaRetriever = MediaMetadataRetriever()
|
||||
metaRetriever.setDataSource(this, it.getUri())
|
||||
it.width = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt()
|
||||
it.height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
|
||||
}
|
||||
|
||||
if (it.width < 0) {
|
||||
it.width = 0
|
||||
}
|
||||
|
||||
if (it.height < 0) {
|
||||
it.height = 0
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupParticipants() {
|
||||
if (participants.isEmpty()) {
|
||||
participants = if (messages.isEmpty()) {
|
||||
getThreadParticipants(threadId, null)
|
||||
} else {
|
||||
messages.first().participants
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupThreadTitle() {
|
||||
val threadTitle = participants.getThreadTitle()
|
||||
if (threadTitle.isNotEmpty()) {
|
||||
supportActionBar?.title = participants.getThreadTitle()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun setupSIMSelector() {
|
||||
val availableSIMs = SubscriptionManager.from(this).activeSubscriptionInfoList ?: return
|
||||
if (availableSIMs.size > 1) {
|
||||
availableSIMs.forEachIndexed { index, subscriptionInfo ->
|
||||
var label = subscriptionInfo.displayName.toString()
|
||||
var label = subscriptionInfo.displayName?.toString() ?: ""
|
||||
if (subscriptionInfo.number?.isNotEmpty() == true) {
|
||||
label += " (${subscriptionInfo.number})"
|
||||
}
|
||||
|
@ -329,6 +397,10 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
if (numbers.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
currentSIMCardIndex = availableSIMs.indexOfFirstOrNull { it.subscriptionId == config.getUseSIMIdAtNumber(numbers.first()) } ?: 0
|
||||
|
||||
thread_select_sim_icon.applyColorFilter(config.textColor)
|
||||
|
@ -396,11 +468,20 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
|
||||
private fun showSelectedContacts() {
|
||||
val adjustedColor = getAdjustedPrimaryColor()
|
||||
|
||||
val views = ArrayList<View>()
|
||||
participants.forEach {
|
||||
val contact = it
|
||||
layoutInflater.inflate(R.layout.item_selected_contact, null).apply {
|
||||
val selectedContactBg = resources.getDrawable(R.drawable.item_selected_contact_background)
|
||||
(selectedContactBg as LayerDrawable).findDrawableByLayerId(R.id.selected_contact_bg).applyColorFilter(adjustedColor)
|
||||
selected_contact_holder.background = selectedContactBg
|
||||
|
||||
selected_contact_name.text = contact.name
|
||||
selected_contact_name.setTextColor(adjustedColor.getContrastColor())
|
||||
selected_contact_remove.applyColorFilter(adjustedColor.getContrastColor())
|
||||
|
||||
selected_contact_remove.setOnClickListener {
|
||||
if (contact.rawId != participants.first().rawId) {
|
||||
removeSelectedContact(contact.rawId)
|
||||
|
@ -424,7 +505,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
|
||||
private fun markAsUnread() {
|
||||
ensureBackgroundThread {
|
||||
conversationsDB.markUnread(threadId.toLong())
|
||||
conversationsDB.markUnread(threadId)
|
||||
markThreadMessagesUnread(threadId)
|
||||
runOnUiThread {
|
||||
finish()
|
||||
|
@ -462,13 +543,17 @@ class ThreadActivity : SimpleActivity() {
|
|||
items.add(message)
|
||||
|
||||
if (message.type == Telephony.Sms.MESSAGE_TYPE_FAILED) {
|
||||
items.add(ThreadError(message.id))
|
||||
items.add(ThreadError(message.id, message.body))
|
||||
}
|
||||
|
||||
if (message.type == Telephony.Sms.MESSAGE_TYPE_OUTBOX) {
|
||||
items.add(ThreadSending(message.id))
|
||||
}
|
||||
|
||||
if (!message.read) {
|
||||
hadUnreadItems = true
|
||||
markMessageRead(message.id, message.isMMS)
|
||||
conversationsDB.markRead(threadId.toLong())
|
||||
conversationsDB.markRead(threadId)
|
||||
}
|
||||
|
||||
if (i == cnt - 1 && message.type == Telephony.Sms.MESSAGE_TYPE_SENT) {
|
||||
|
@ -562,6 +647,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
|
||||
val settings = Settings()
|
||||
settings.useSystemSending = true
|
||||
settings.deliveryReports = true
|
||||
|
||||
val SIMId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId
|
||||
if (SIMId != null) {
|
||||
|
@ -582,19 +668,35 @@ class ThreadActivity : SimpleActivity() {
|
|||
message.addMedia(byteArray, mimeType)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
} catch (e: Error) {
|
||||
toast(e.localizedMessage ?: getString(R.string.unknown_error_occurred))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
transaction.sendNewMessage(message, threadId.toLong())
|
||||
val smsSentIntent = Intent(this, SmsStatusSentReceiver::class.java)
|
||||
val deliveredIntent = Intent(this, SmsStatusDeliveredReceiver::class.java)
|
||||
|
||||
transaction.setExplicitBroadcastForSentSms(smsSentIntent)
|
||||
transaction.setExplicitBroadcastForDeliveredSms(deliveredIntent)
|
||||
|
||||
refreshedSinceSent = false
|
||||
transaction.sendNewMessage(message, threadId)
|
||||
thread_type_message.setText("")
|
||||
attachmentUris.clear()
|
||||
thread_attachments_holder.beGone()
|
||||
thread_attachments_wrapper.removeAllViews()
|
||||
|
||||
Handler().postDelayed({
|
||||
if (!refreshedSinceSent) {
|
||||
refreshMessages()
|
||||
}
|
||||
}, 2000)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
} catch (e: Error) {
|
||||
toast(e.localizedMessage ?: getString(R.string.unknown_error_occurred))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,13 +754,30 @@ class ThreadActivity : SimpleActivity() {
|
|||
showSelectedContacts()
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Subscribe(threadMode = ThreadMode.ASYNC)
|
||||
fun refreshMessages(event: Events.RefreshMessages) {
|
||||
refreshedSinceSent = true
|
||||
if (isActivityVisible) {
|
||||
notificationManager.cancel(threadId)
|
||||
notificationManager.cancel(threadId.hashCode())
|
||||
}
|
||||
|
||||
val lastMaxId = messages.maxByOrNull { it.id }?.id ?: 0L
|
||||
messages = getMessages(threadId)
|
||||
|
||||
messages.filter { !it.isReceivedMessage() && it.id > lastMaxId }.forEach { latestMessage ->
|
||||
// subscriptionIds seem to be not filled out at sending with multiple SIM cards, so fill it manually
|
||||
if (SubscriptionManager.from(this).activeSubscriptionInfoList?.size ?: 0 > 1) {
|
||||
val SIMId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId
|
||||
if (SIMId != null) {
|
||||
updateMessageSubscriptionId(latestMessage.id, SIMId)
|
||||
latestMessage.subscriptionId = SIMId
|
||||
}
|
||||
}
|
||||
|
||||
messagesDB.insertOrIgnore(latestMessage)
|
||||
}
|
||||
|
||||
setupAdapter()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,16 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Filter
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import com.simplemobiletools.commons.extensions.darkenColor
|
||||
import com.simplemobiletools.commons.extensions.getContrastColor
|
||||
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.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
|
||||
class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: ArrayList<SimpleContact>) : ArrayAdapter<SimpleContact>(activity, 0, contacts) {
|
||||
var resultList = ArrayList<SimpleContact>()
|
||||
|
@ -30,8 +34,13 @@ class AutoCompleteTextViewAdapter(val activity: SimpleActivity, val contacts: Ar
|
|||
isFocusable = false
|
||||
}
|
||||
|
||||
val backgroundColor = activity.config.backgroundColor
|
||||
findViewById<TextView>(R.id.item_contact_name).text = contact.name
|
||||
findViewById<TextView>(R.id.item_contact_number).text = contact.phoneNumbers.first()
|
||||
findViewById<RelativeLayout>(R.id.item_contact_holder).setBackgroundColor(backgroundColor.darkenColor())
|
||||
|
||||
findViewById<TextView>(R.id.item_contact_name).setTextColor(backgroundColor.getContrastColor())
|
||||
findViewById<TextView>(R.id.item_contact_number).setTextColor(backgroundColor.getContrastColor())
|
||||
|
||||
SimpleContactsHelper(context).loadContactImage(contact.photoUri, findViewById(R.id.item_contact_image), contact.name)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.simplemobiletools.smsmessenger.adapters
|
|||
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.Menu
|
||||
|
@ -37,8 +38,10 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
|||
|
||||
override fun prepareActionMode(menu: Menu) {
|
||||
menu.apply {
|
||||
findItem(R.id.cab_add_number_to_contact).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_block_number).isVisible = isNougatPlus()
|
||||
findItem(R.id.cab_add_number_to_contact).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_dial_number).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
|
||||
findItem(R.id.cab_copy_number).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,8 +53,10 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
|||
when (id) {
|
||||
R.id.cab_add_number_to_contact -> addNumberToContact()
|
||||
R.id.cab_block_number -> askConfirmBlock()
|
||||
R.id.cab_select_all -> selectAll()
|
||||
R.id.cab_dial_number -> dialNumber()
|
||||
R.id.cab_copy_number -> copyNumberToClipboard()
|
||||
R.id.cab_delete -> askConfirmDelete()
|
||||
R.id.cab_select_all -> selectAll()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,9 +64,9 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
|||
|
||||
override fun getIsItemSelectable(position: Int) = true
|
||||
|
||||
override fun getItemSelectionKey(position: Int) = conversations.getOrNull(position)?.thread_id
|
||||
override fun getItemSelectionKey(position: Int) = conversations.getOrNull(position)?.hashCode()
|
||||
|
||||
override fun getItemKeyPosition(key: Int) = conversations.indexOfFirst { it.thread_id == key }
|
||||
override fun getItemKeyPosition(key: Int) = conversations.indexOfFirst { it.hashCode() == key }
|
||||
|
||||
override fun onActionModeCreated() {}
|
||||
|
||||
|
@ -110,6 +115,26 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
|||
}
|
||||
}
|
||||
|
||||
private fun dialNumber() {
|
||||
val conversation = getSelectedItems().firstOrNull() ?: return
|
||||
Intent(Intent.ACTION_DIAL).apply {
|
||||
data = Uri.fromParts("tel", conversation.phoneNumber, null)
|
||||
|
||||
if (resolveActivity(activity.packageManager) != null) {
|
||||
activity.startActivity(this)
|
||||
finishActMode()
|
||||
} else {
|
||||
activity.toast(R.string.no_app_found)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyNumberToClipboard() {
|
||||
val conversation = getSelectedItems().firstOrNull() ?: return
|
||||
activity.copyToClipboard(conversation.phoneNumber)
|
||||
finishActMode()
|
||||
}
|
||||
|
||||
private fun askConfirmDelete() {
|
||||
val itemsCnt = selectedKeys.size
|
||||
val items = resources.getQuantityString(R.plurals.delete_conversations, itemsCnt, itemsCnt)
|
||||
|
@ -129,13 +154,17 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
|||
return
|
||||
}
|
||||
|
||||
val conversationsToRemove = conversations.filter { selectedKeys.contains(it.thread_id) } as ArrayList<Conversation>
|
||||
val conversationsToRemove = conversations.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
val positions = getSelectedItemPositions()
|
||||
conversationsToRemove.forEach {
|
||||
activity.deleteConversation(it.thread_id)
|
||||
activity.notificationManager.cancel(it.thread_id)
|
||||
activity.deleteConversation(it.threadId)
|
||||
activity.notificationManager.cancel(it.hashCode())
|
||||
}
|
||||
|
||||
try {
|
||||
conversations.removeAll(conversationsToRemove)
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
conversations.removeAll(conversationsToRemove)
|
||||
|
||||
activity.runOnUiThread {
|
||||
if (conversationsToRemove.isEmpty()) {
|
||||
|
@ -165,7 +194,7 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
|||
}
|
||||
}
|
||||
|
||||
private fun getSelectedItems() = conversations.filter { selectedKeys.contains(it.thread_id) } as ArrayList<Conversation>
|
||||
private fun getSelectedItems() = conversations.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
|
@ -190,7 +219,7 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
|||
|
||||
private fun setupView(view: View, conversation: Conversation) {
|
||||
view.apply {
|
||||
conversation_frame.isSelected = selectedKeys.contains(conversation.thread_id)
|
||||
conversation_frame.isSelected = selectedKeys.contains(conversation.hashCode())
|
||||
|
||||
conversation_address.apply {
|
||||
text = conversation.title
|
||||
|
@ -203,7 +232,7 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis
|
|||
}
|
||||
|
||||
conversation_date.apply {
|
||||
text = conversation.date.formatDateOrTime(context, true)
|
||||
text = conversation.date.formatDateOrTime(context, true, false)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 0.8f)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package com.simplemobiletools.smsmessenger.adapters
|
||||
|
||||
import android.util.TypedValue
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.bumptech.glide.Glide
|
||||
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
|
||||
import com.simplemobiletools.commons.extensions.getTextSize
|
||||
import com.simplemobiletools.commons.extensions.highlightTextPart
|
||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.models.SearchResult
|
||||
import kotlinx.android.synthetic.main.item_search_result.view.*
|
||||
import java.util.*
|
||||
|
||||
class SearchResultsAdapter(
|
||||
activity: SimpleActivity, var searchResults: ArrayList<SearchResult>, recyclerView: MyRecyclerView, highlightText: String, itemClick: (Any) -> Unit
|
||||
) : MyRecyclerViewAdapter(activity, recyclerView, null, itemClick) {
|
||||
|
||||
private var fontSize = activity.getTextSize()
|
||||
private var textToHighlight = highlightText
|
||||
|
||||
override fun getActionMenuId() = 0
|
||||
|
||||
override fun prepareActionMode(menu: Menu) {}
|
||||
|
||||
override fun actionItemPressed(id: Int) {}
|
||||
|
||||
override fun getSelectableItemCount() = searchResults.size
|
||||
|
||||
override fun getIsItemSelectable(position: Int) = false
|
||||
|
||||
override fun getItemSelectionKey(position: Int) = searchResults.getOrNull(position)?.hashCode()
|
||||
|
||||
override fun getItemKeyPosition(key: Int) = searchResults.indexOfFirst { it.hashCode() == key }
|
||||
|
||||
override fun onActionModeCreated() {}
|
||||
|
||||
override fun onActionModeDestroyed() {}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_search_result, parent)
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val searchResult = searchResults[position]
|
||||
holder.bindView(searchResult, true, false) { itemView, layoutPosition ->
|
||||
setupView(itemView, searchResult)
|
||||
}
|
||||
bindViewHolder(holder)
|
||||
}
|
||||
|
||||
override fun getItemCount() = searchResults.size
|
||||
|
||||
fun updateItems(newItems: ArrayList<SearchResult>, highlightText: String = "") {
|
||||
if (newItems.hashCode() != searchResults.hashCode()) {
|
||||
searchResults = newItems.clone() as ArrayList<SearchResult>
|
||||
textToHighlight = highlightText
|
||||
notifyDataSetChanged()
|
||||
} else if (textToHighlight != highlightText) {
|
||||
textToHighlight = highlightText
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupView(view: View, searchResult: SearchResult) {
|
||||
view.apply {
|
||||
search_result_title.apply {
|
||||
text = searchResult.title.highlightTextPart(textToHighlight, adjustedPrimaryColor)
|
||||
setTextColor(textColor)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 1.2f)
|
||||
}
|
||||
|
||||
search_result_snippet.apply {
|
||||
text = searchResult.snippet.highlightTextPart(textToHighlight, adjustedPrimaryColor)
|
||||
setTextColor(textColor)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 0.9f)
|
||||
}
|
||||
|
||||
search_result_date.apply {
|
||||
text = searchResult.date
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize * 0.8f)
|
||||
}
|
||||
|
||||
SimpleContactsHelper(context).loadContactImage(searchResult.photoUri, search_result_image, searchResult.title)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
if (!activity.isDestroyed && !activity.isFinishing && holder.itemView.search_result_image != null) {
|
||||
Glide.with(activity).clear(holder.itemView.search_result_image)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import com.simplemobiletools.commons.views.FastScroller
|
|||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
|
||||
import com.simplemobiletools.smsmessenger.dialogs.SelectTextDialog
|
||||
import com.simplemobiletools.smsmessenger.extensions.deleteMessage
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
|
@ -37,10 +38,14 @@ import kotlinx.android.synthetic.main.item_received_message.view.*
|
|||
import kotlinx.android.synthetic.main.item_received_unknown_attachment.view.*
|
||||
import kotlinx.android.synthetic.main.item_sent_unknown_attachment.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_date_time.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_error.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_sending.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_success.view.*
|
||||
|
||||
class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem>, recyclerView: MyRecyclerView, fastScroller: FastScroller,
|
||||
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
||||
class ThreadAdapter(
|
||||
activity: SimpleActivity, var messages: ArrayList<ThreadItem>, recyclerView: MyRecyclerView, fastScroller: FastScroller,
|
||||
itemClick: (Any) -> Unit
|
||||
) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
||||
private val roundedCornersRadius = resources.getDimension(R.dimen.normal_margin).toInt()
|
||||
private var fontSize = activity.getTextSize()
|
||||
|
||||
|
@ -58,6 +63,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
menu.apply {
|
||||
findItem(R.id.cab_copy_to_clipboard).isVisible = isOneItemSelected
|
||||
findItem(R.id.cab_share).isVisible = isOneItemSelected
|
||||
findItem(R.id.cab_select_text).isVisible = isOneItemSelected
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,8 +75,9 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
when (id) {
|
||||
R.id.cab_copy_to_clipboard -> copyToClipboard()
|
||||
R.id.cab_share -> shareText()
|
||||
R.id.cab_select_all -> selectAll()
|
||||
R.id.cab_select_text -> selectText()
|
||||
R.id.cab_delete -> askConfirmDelete()
|
||||
R.id.cab_select_all -> selectAll()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,9 +85,9 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
|
||||
override fun getIsItemSelectable(position: Int) = !isThreadDateTime(position)
|
||||
|
||||
override fun getItemSelectionKey(position: Int) = (messages.getOrNull(position) as? Message)?.id
|
||||
override fun getItemSelectionKey(position: Int) = (messages.getOrNull(position) as? Message)?.hashCode()
|
||||
|
||||
override fun getItemKeyPosition(key: Int) = messages.indexOfFirst { (it as? Message)?.id == key }
|
||||
override fun getItemKeyPosition(key: Int) = messages.indexOfFirst { (it as? Message)?.hashCode() == key }
|
||||
|
||||
override fun onActionModeCreated() {}
|
||||
|
||||
|
@ -92,6 +99,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
THREAD_RECEIVED_MESSAGE -> R.layout.item_received_message
|
||||
THREAD_SENT_MESSAGE_ERROR -> R.layout.item_thread_error
|
||||
THREAD_SENT_MESSAGE_SUCCESS -> R.layout.item_thread_success
|
||||
THREAD_SENT_MESSAGE_SENDING -> R.layout.item_thread_sending
|
||||
else -> R.layout.item_sent_message
|
||||
}
|
||||
return createViewHolder(layout, parent)
|
||||
|
@ -99,11 +107,15 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = messages[position]
|
||||
holder.bindView(item, true, item is Message) { itemView, layoutPosition ->
|
||||
val isClickable = item is ThreadError || item is Message
|
||||
val isLongClickable = item is Message
|
||||
holder.bindView(item, isClickable, isLongClickable) { itemView, layoutPosition ->
|
||||
when (item) {
|
||||
is ThreadDateTime -> setupDateTime(itemView, item)
|
||||
is ThreadSuccess -> setupThreadSuccess(itemView)
|
||||
!is ThreadError -> setupView(itemView, item as Message)
|
||||
is ThreadError -> setupThreadError(itemView)
|
||||
is ThreadSending -> setupThreadSending(itemView)
|
||||
else -> setupView(itemView, item as Message)
|
||||
}
|
||||
}
|
||||
bindViewHolder(holder)
|
||||
|
@ -118,23 +130,38 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
(messages[position] as? Message)?.isReceivedMessage() == true -> THREAD_RECEIVED_MESSAGE
|
||||
item is ThreadError -> THREAD_SENT_MESSAGE_ERROR
|
||||
item is ThreadSuccess -> THREAD_SENT_MESSAGE_SUCCESS
|
||||
item is ThreadSending -> THREAD_SENT_MESSAGE_SENDING
|
||||
else -> THREAD_SENT_MESSAGE
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyToClipboard() {
|
||||
val firstItem = getSelectedItems().first() as? Message ?: return
|
||||
val firstItem = getSelectedItems().firstOrNull() as? Message ?: return
|
||||
activity.copyToClipboard(firstItem.body)
|
||||
}
|
||||
|
||||
private fun shareText() {
|
||||
val firstItem = getSelectedItems().first() as? Message ?: return
|
||||
val firstItem = getSelectedItems().firstOrNull() as? Message ?: return
|
||||
activity.shareTextIntent(firstItem.body)
|
||||
}
|
||||
|
||||
private fun selectText() {
|
||||
val firstItem = getSelectedItems().firstOrNull() as? Message ?: return
|
||||
if (firstItem.body.trim().isNotEmpty()) {
|
||||
SelectTextDialog(activity, firstItem.body)
|
||||
}
|
||||
}
|
||||
|
||||
private fun askConfirmDelete() {
|
||||
val itemsCnt = selectedKeys.size
|
||||
val items = resources.getQuantityString(R.plurals.delete_messages, itemsCnt, itemsCnt)
|
||||
|
||||
// not sure how we can get UnknownFormatConversionException here, so show the error and hope that someone reports it
|
||||
val items = try {
|
||||
resources.getQuantityString(R.plurals.delete_messages, itemsCnt, itemsCnt)
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
return
|
||||
}
|
||||
|
||||
val baseString = R.string.deletion_confirmation
|
||||
val question = String.format(resources.getString(baseString), items)
|
||||
|
@ -151,7 +178,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
return
|
||||
}
|
||||
|
||||
val messagesToRemove = messages.filter { selectedKeys.contains((it as? Message)?.id ?: 0) } as ArrayList<ThreadItem>
|
||||
val messagesToRemove = getSelectedItems()
|
||||
val positions = getSelectedItemPositions()
|
||||
messagesToRemove.forEach {
|
||||
activity.deleteMessage((it as Message).id, it.isMMS)
|
||||
|
@ -168,20 +195,23 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
}
|
||||
}
|
||||
|
||||
private fun getSelectedItems() = messages.filter { selectedKeys.contains((it as? Message)?.id ?: 0) } as ArrayList<ThreadItem>
|
||||
private fun getSelectedItems() = messages.filter { selectedKeys.contains((it as? Message)?.hashCode() ?: 0) } as ArrayList<ThreadItem>
|
||||
|
||||
private fun isThreadDateTime(position: Int) = messages.getOrNull(position) is ThreadDateTime
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
if (!activity.isDestroyed && !activity.isFinishing && holder.itemView.thread_message_sender_photo != null) {
|
||||
Glide.with(activity).clear(holder.itemView.thread_message_sender_photo)
|
||||
fun updateMessages(newMessages: ArrayList<ThreadItem>) {
|
||||
val oldHashCode = messages.hashCode()
|
||||
val newHashCode = newMessages.hashCode()
|
||||
if (newHashCode != oldHashCode) {
|
||||
messages = newMessages
|
||||
notifyDataSetChanged()
|
||||
recyclerView.scrollToPosition(messages.size - 1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupView(view: View, message: Message) {
|
||||
view.apply {
|
||||
thread_message_holder.isSelected = selectedKeys.contains(message.id)
|
||||
thread_message_holder.isSelected = selectedKeys.contains(message.hashCode())
|
||||
thread_message_body.apply {
|
||||
text = message.body
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
|
||||
|
@ -196,7 +226,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
} else {
|
||||
thread_message_sender_photo?.beGone()
|
||||
val background = context.getAdjustedPrimaryColor()
|
||||
thread_message_body.background.applyColorFilter(background.adjustAlpha(0.8f))
|
||||
thread_message_body.background.applyColorFilter(background)
|
||||
|
||||
val contrastColor = background.getContrastColor()
|
||||
thread_message_body.setTextColor(contrastColor)
|
||||
|
@ -207,7 +237,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
if (message.attachment?.attachments?.isNotEmpty() == true) {
|
||||
for (attachment in message.attachment.attachments) {
|
||||
val mimetype = attachment.mimetype
|
||||
val uri = attachment.uri
|
||||
val uri = attachment.getUri()
|
||||
if (mimetype.startsWith("image/") || mimetype.startsWith("video/")) {
|
||||
val imageView = layoutInflater.inflate(R.layout.item_attachment_image, null)
|
||||
thread_mesage_attachments_holder.addView(imageView)
|
||||
|
@ -238,7 +268,9 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
}
|
||||
|
||||
builder.into(imageView.attachment_image)
|
||||
imageView.attachment_image.setOnClickListener { launchViewIntent(uri, mimetype, attachment.filename) }
|
||||
imageView.attachment_image.setOnClickListener {
|
||||
launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
} else {
|
||||
if (message.isReceivedMessage()) {
|
||||
val attachmentView = layoutInflater.inflate(R.layout.item_received_unknown_attachment, null).apply {
|
||||
|
@ -247,7 +279,9 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
thread_received_attachment_label.text = attachment.filename
|
||||
}
|
||||
setTextColor(textColor)
|
||||
setOnClickListener { launchViewIntent(uri, mimetype, attachment.filename) }
|
||||
setOnClickListener {
|
||||
launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
thread_mesage_attachments_holder.addView(attachmentView)
|
||||
|
@ -255,12 +289,14 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
val background = context.getAdjustedPrimaryColor()
|
||||
val attachmentView = layoutInflater.inflate(R.layout.item_sent_unknown_attachment, null).apply {
|
||||
thread_sent_attachment_label.apply {
|
||||
this.background.applyColorFilter(background.adjustAlpha(0.8f))
|
||||
this.background.applyColorFilter(background)
|
||||
setTextColor(background.getContrastColor())
|
||||
if (attachment.filename.isNotEmpty()) {
|
||||
thread_sent_attachment_label.text = attachment.filename
|
||||
}
|
||||
setOnClickListener { launchViewIntent(uri, mimetype, attachment.filename) }
|
||||
setOnClickListener {
|
||||
launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
thread_mesage_attachments_holder.addView(attachmentView)
|
||||
|
@ -295,7 +331,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
private fun setupDateTime(view: View, dateTime: ThreadDateTime) {
|
||||
view.apply {
|
||||
thread_date_time.apply {
|
||||
text = dateTime.date.formatDateOrTime(context, false)
|
||||
text = dateTime.date.formatDateOrTime(context, false, false)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
|
||||
}
|
||||
thread_date_time.setTextColor(textColor)
|
||||
|
@ -313,4 +349,22 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
|||
private fun setupThreadSuccess(view: View) {
|
||||
view.thread_success.applyColorFilter(textColor)
|
||||
}
|
||||
|
||||
private fun setupThreadError(view: View) {
|
||||
view.thread_error.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize - 4)
|
||||
}
|
||||
|
||||
private fun setupThreadSending(view: View) {
|
||||
view.thread_sending.apply {
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
|
||||
setTextColor(textColor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
if (!activity.isDestroyed && !activity.isFinishing && holder.itemView.thread_message_sender_photo != null) {
|
||||
Glide.with(activity).clear(holder.itemView.thread_message_sender_photo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,31 @@ import android.content.Context
|
|||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.simplemobiletools.smsmessenger.helpers.Converters
|
||||
import com.simplemobiletools.smsmessenger.interfaces.AttachmentsDao
|
||||
import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
|
||||
import com.simplemobiletools.smsmessenger.interfaces.MessageAttachmentsDao
|
||||
import com.simplemobiletools.smsmessenger.interfaces.MessagesDao
|
||||
import com.simplemobiletools.smsmessenger.models.Attachment
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
import com.simplemobiletools.smsmessenger.models.Message
|
||||
import com.simplemobiletools.smsmessenger.models.MessageAttachment
|
||||
|
||||
@Database(entities = [(Conversation::class)], version = 1)
|
||||
@Database(entities = [Conversation::class, Attachment::class, MessageAttachment::class, Message::class], version = 3)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class MessagesDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun ConversationsDao(): ConversationsDao
|
||||
|
||||
abstract fun AttachmentsDao(): AttachmentsDao
|
||||
|
||||
abstract fun MessageAttachmentsDao(): MessageAttachmentsDao
|
||||
|
||||
abstract fun MessagesDao(): MessagesDao
|
||||
|
||||
companion object {
|
||||
private var db: MessagesDatabase? = null
|
||||
|
||||
|
@ -20,11 +37,44 @@ abstract class MessagesDatabase : RoomDatabase() {
|
|||
synchronized(MessagesDatabase::class) {
|
||||
if (db == null) {
|
||||
db = Room.databaseBuilder(context.applicationContext, MessagesDatabase::class.java, "conversations.db")
|
||||
.fallbackToDestructiveMigration()
|
||||
.addMigrations(MIGRATION_1_2)
|
||||
.addMigrations(MIGRATION_2_3)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
return db!!
|
||||
}
|
||||
|
||||
private val MIGRATION_1_2 = object : Migration(1, 2) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.apply {
|
||||
execSQL("CREATE TABLE IF NOT EXISTS `messages` (`id` INTEGER PRIMARY KEY NOT NULL, `body` TEXT NOT NULL, `type` INTEGER NOT NULL, `participants` TEXT NOT NULL, `date` INTEGER NOT NULL, `read` INTEGER NOT NULL, `thread_id` INTEGER NOT NULL, `is_mms` INTEGER NOT NULL, `attachment` TEXT, `sender_name` TEXT NOT NULL, `sender_photo_uri` TEXT NOT NULL, `subscription_id` INTEGER NOT NULL)")
|
||||
|
||||
execSQL("CREATE TABLE IF NOT EXISTS `message_attachments` (`id` INTEGER PRIMARY KEY NOT NULL, `text` TEXT NOT NULL, `attachments` TEXT NOT NULL)")
|
||||
|
||||
execSQL("CREATE TABLE IF NOT EXISTS `attachments` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `message_id` INTEGER NOT NULL, `uri_string` TEXT NOT NULL, `mimetype` TEXT NOT NULL, `width` INTEGER NOT NULL, `height` INTEGER NOT NULL, `filename` TEXT NOT NULL)")
|
||||
execSQL("CREATE UNIQUE INDEX `index_attachments_message_id` ON `attachments` (`message_id`)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_2_3 = object : Migration(2, 3) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.apply {
|
||||
execSQL("CREATE TABLE conversations_new (`thread_id` INTEGER NOT NULL PRIMARY KEY, `snippet` TEXT NOT NULL, `date` INTEGER NOT NULL, `read` INTEGER NOT NULL, `title` TEXT NOT NULL, `photo_uri` TEXT NOT NULL, `is_group_conversation` INTEGER NOT NULL, `phone_number` TEXT NOT NULL)")
|
||||
|
||||
execSQL("INSERT OR IGNORE INTO conversations_new (thread_id, snippet, date, read, title, photo_uri, is_group_conversation, phone_number) " +
|
||||
"SELECT thread_id, snippet, date, read, title, photo_uri, is_group_conversation, phone_number FROM conversations")
|
||||
|
||||
execSQL("DROP TABLE conversations")
|
||||
|
||||
execSQL("ALTER TABLE conversations_new RENAME TO conversations")
|
||||
|
||||
execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_conversations_id` ON `conversations` (`thread_id`)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package com.simplemobiletools.smsmessenger.dialogs
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import kotlinx.android.synthetic.main.dialog_select_text.view.*
|
||||
|
||||
// helper dialog for selecting just a part of a message, not copying the whole into clipboard
|
||||
class SelectTextDialog(val activity: BaseSimpleActivity, val text: String) {
|
||||
init {
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_select_text, null).apply {
|
||||
dialog_select_text_value.text = text
|
||||
}
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok) { dialog, which -> { } }
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,8 +2,5 @@ package com.simplemobiletools.smsmessenger.extensions
|
|||
|
||||
import android.text.TextUtils
|
||||
import com.simplemobiletools.commons.models.SimpleContact
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
|
||||
fun ArrayList<SimpleContact>.getThreadTitle() = TextUtils.join(", ", map { it.name }.toTypedArray())
|
||||
|
||||
fun ArrayList<Conversation>.getHashToCompare() = map { it.getStringToCompare() }.hashCode()
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.app.PendingIntent
|
|||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.graphics.Bitmap
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioManager
|
||||
|
@ -27,7 +28,10 @@ import com.simplemobiletools.smsmessenger.R
|
|||
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
|
||||
import com.simplemobiletools.smsmessenger.databases.MessagesDatabase
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.interfaces.AttachmentsDao
|
||||
import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
|
||||
import com.simplemobiletools.smsmessenger.interfaces.MessageAttachmentsDao
|
||||
import com.simplemobiletools.smsmessenger.interfaces.MessagesDao
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import com.simplemobiletools.smsmessenger.receivers.DirectReplyReceiver
|
||||
import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver
|
||||
|
@ -41,7 +45,13 @@ fun Context.getMessagessDB() = MessagesDatabase.getInstance(this)
|
|||
|
||||
val Context.conversationsDB: ConversationsDao get() = getMessagessDB().ConversationsDao()
|
||||
|
||||
fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
||||
val Context.attachmentsDB: AttachmentsDao get() = getMessagessDB().AttachmentsDao()
|
||||
|
||||
val Context.messageAttachmentsDB: MessageAttachmentsDao get() = getMessagessDB().MessageAttachmentsDao()
|
||||
|
||||
val Context.messagesDB: MessagesDao get() = getMessagessDB().MessagesDao()
|
||||
|
||||
fun Context.getMessages(threadId: Long): ArrayList<Message> {
|
||||
val uri = Sms.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Sms._ID,
|
||||
|
@ -76,17 +86,17 @@ fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
|||
return@queryCursor
|
||||
}
|
||||
|
||||
val id = cursor.getIntValue(Sms._ID)
|
||||
val id = cursor.getLongValue(Sms._ID)
|
||||
val body = cursor.getStringValue(Sms.BODY)
|
||||
val type = cursor.getIntValue(Sms.TYPE)
|
||||
val namePhoto = getNameAndPhotoFromPhoneNumber(senderNumber)
|
||||
val senderName = namePhoto?.name ?: ""
|
||||
val photoUri = namePhoto?.photoUri ?: ""
|
||||
val senderName = namePhoto.name
|
||||
val photoUri = namePhoto.photoUri ?: ""
|
||||
val date = (cursor.getLongValue(Sms.DATE) / 1000).toInt()
|
||||
val read = cursor.getIntValue(Sms.READ) == 1
|
||||
val thread = cursor.getIntValue(Sms.THREAD_ID)
|
||||
val thread = cursor.getLongValue(Sms.THREAD_ID)
|
||||
val subscriptionId = cursor.getIntValue(Sms.SUBSCRIPTION_ID)
|
||||
val participant = SimpleContact(0, 0, senderName, photoUri, arrayListOf(senderNumber))
|
||||
val participant = SimpleContact(0, 0, senderName, photoUri, arrayListOf(senderNumber), ArrayList(), ArrayList())
|
||||
val isMMS = false
|
||||
val message = Message(id, body, type, arrayListOf(participant), date, read, thread, isMMS, null, senderName, photoUri, subscriptionId)
|
||||
messages.add(message)
|
||||
|
@ -100,7 +110,7 @@ fun Context.getMessages(threadId: Int): ArrayList<Message> {
|
|||
}
|
||||
|
||||
// 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: Long? = null, sortOrder: String? = null): ArrayList<Message> {
|
||||
val uri = Mms.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Mms._ID,
|
||||
|
@ -125,13 +135,13 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<
|
|||
|
||||
val messages = ArrayList<Message>()
|
||||
val contactsMap = HashMap<Int, SimpleContact>()
|
||||
val threadParticipants = HashMap<Int, ArrayList<SimpleContact>>()
|
||||
val threadParticipants = HashMap<Long, ArrayList<SimpleContact>>()
|
||||
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
|
||||
val mmsId = cursor.getIntValue(Mms._ID)
|
||||
val mmsId = cursor.getLongValue(Mms._ID)
|
||||
val type = cursor.getIntValue(Mms.MESSAGE_BOX)
|
||||
val date = cursor.getLongValue(Mms.DATE).toInt()
|
||||
val read = cursor.getIntValue(Mms.READ) == 1
|
||||
val threadId = cursor.getIntValue(Mms.THREAD_ID)
|
||||
val threadId = cursor.getLongValue(Mms.THREAD_ID)
|
||||
val subscriptionId = cursor.getIntValue(Mms.SUBSCRIPTION_ID)
|
||||
val participants = if (threadParticipants.containsKey(threadId)) {
|
||||
threadParticipants[threadId]!!
|
||||
|
@ -143,17 +153,15 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<
|
|||
|
||||
val isMMS = true
|
||||
val attachment = getMmsAttachment(mmsId)
|
||||
val body = attachment?.text ?: ""
|
||||
val body = attachment.text
|
||||
var senderName = ""
|
||||
var senderPhotoUri = ""
|
||||
|
||||
if (type != Mms.MESSAGE_BOX_SENT && type != Mms.MESSAGE_BOX_FAILED) {
|
||||
val number = getMMSSender(mmsId)
|
||||
val namePhoto = getNameAndPhotoFromPhoneNumber(number)
|
||||
if (namePhoto != null) {
|
||||
senderName = namePhoto.name
|
||||
senderPhotoUri = namePhoto.photoUri ?: ""
|
||||
}
|
||||
senderName = namePhoto.name
|
||||
senderPhotoUri = namePhoto.photoUri ?: ""
|
||||
}
|
||||
|
||||
val message = Message(mmsId, body, type, participants, date, read, threadId, isMMS, attachment, senderName, senderPhotoUri, subscriptionId)
|
||||
|
@ -167,7 +175,7 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList<
|
|||
return messages
|
||||
}
|
||||
|
||||
fun Context.getMMSSender(msgId: Int): String {
|
||||
fun Context.getMMSSender(msgId: Long): String {
|
||||
val uri = Uri.parse("${Mms.CONTENT_URI}/$msgId/addr")
|
||||
val projection = arrayOf(
|
||||
Mms.Addr.ADDRESS
|
||||
|
@ -185,7 +193,7 @@ fun Context.getMMSSender(msgId: Int): String {
|
|||
return ""
|
||||
}
|
||||
|
||||
fun Context.getConversations(threadId: Long? = null): ArrayList<Conversation> {
|
||||
fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList<SimpleContact> = ArrayList()): ArrayList<Conversation> {
|
||||
val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true")
|
||||
val projection = arrayOf(
|
||||
Threads._ID,
|
||||
|
@ -208,7 +216,7 @@ fun Context.getConversations(threadId: Long? = null): ArrayList<Conversation> {
|
|||
val simpleContactHelper = SimpleContactsHelper(this)
|
||||
val blockedNumbers = getBlockedNumbers()
|
||||
queryCursor(uri, projection, selection, selectionArgs, sortOrder, true) { cursor ->
|
||||
val id = cursor.getIntValue(Threads._ID)
|
||||
val id = cursor.getLongValue(Threads._ID)
|
||||
var snippet = cursor.getStringValue(Threads.SNIPPET) ?: ""
|
||||
if (snippet.isEmpty()) {
|
||||
snippet = getThreadSnippet(id)
|
||||
|
@ -226,12 +234,12 @@ fun Context.getConversations(threadId: Long? = null): ArrayList<Conversation> {
|
|||
return@queryCursor
|
||||
}
|
||||
|
||||
val names = getThreadContactNames(phoneNumbers)
|
||||
val names = getThreadContactNames(phoneNumbers, privateContacts)
|
||||
val title = TextUtils.join(", ", names.toTypedArray())
|
||||
val photoUri = if (phoneNumbers.size == 1) simpleContactHelper.getPhotoUriFromPhoneNumber(phoneNumbers.first()) else ""
|
||||
val isGroupConversation = phoneNumbers.size > 1
|
||||
val read = cursor.getIntValue(Threads.READ) == 1
|
||||
val conversation = Conversation(null, id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first())
|
||||
val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first())
|
||||
conversations.add(conversation)
|
||||
}
|
||||
|
||||
|
@ -241,7 +249,7 @@ fun Context.getConversations(threadId: Long? = null): ArrayList<Conversation> {
|
|||
|
||||
// based on https://stackoverflow.com/a/6446831/1967672
|
||||
@SuppressLint("NewApi")
|
||||
fun Context.getMmsAttachment(id: Int): MessageAttachment? {
|
||||
fun Context.getMmsAttachment(id: Long): MessageAttachment {
|
||||
val uri = if (isQPlus()) {
|
||||
Mms.Part.CONTENT_URI
|
||||
} else {
|
||||
|
@ -259,15 +267,15 @@ fun Context.getMmsAttachment(id: Int): MessageAttachment? {
|
|||
|
||||
var attachmentName = ""
|
||||
queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val partId = cursor.getStringValue(Mms._ID)
|
||||
val partId = cursor.getLongValue(Mms._ID)
|
||||
val mimetype = cursor.getStringValue(Mms.Part.CONTENT_TYPE)
|
||||
if (mimetype == "text/plain") {
|
||||
messageAttachment.text = cursor.getStringValue(Mms.Part.TEXT) ?: ""
|
||||
} else if (mimetype.startsWith("image/") || mimetype.startsWith("video/")) {
|
||||
val attachment = Attachment(Uri.withAppendedPath(uri, partId), mimetype, 0, 0, "")
|
||||
val attachment = Attachment(partId, id, Uri.withAppendedPath(uri, partId.toString()).toString(), mimetype, 0, 0, "")
|
||||
messageAttachment.attachments.add(attachment)
|
||||
} else if (mimetype != "application/smil") {
|
||||
val attachment = Attachment(Uri.withAppendedPath(uri, partId), mimetype, 0, 0, attachmentName)
|
||||
val attachment = Attachment(partId, id, Uri.withAppendedPath(uri, partId.toString()).toString(), mimetype, 0, 0, attachmentName)
|
||||
messageAttachment.attachments.add(attachment)
|
||||
} else {
|
||||
val text = cursor.getStringValue(Mms.Part.TEXT)
|
||||
|
@ -286,7 +294,7 @@ fun Context.getLatestMMS(): Message? {
|
|||
return getMMS(sortOrder = sortOrder).firstOrNull()
|
||||
}
|
||||
|
||||
fun Context.getThreadSnippet(threadId: Int): String {
|
||||
fun Context.getThreadSnippet(threadId: Long): String {
|
||||
val sortOrder = "${Mms.DATE} DESC LIMIT 1"
|
||||
val latestMms = getMMS(threadId, sortOrder).firstOrNull()
|
||||
var snippet = latestMms?.body ?: ""
|
||||
|
@ -313,7 +321,29 @@ fun Context.getThreadSnippet(threadId: Int): String {
|
|||
return snippet
|
||||
}
|
||||
|
||||
fun Context.getThreadParticipants(threadId: Int, contactsMap: HashMap<Int, SimpleContact>?): ArrayList<SimpleContact> {
|
||||
fun Context.getMessageRecipientAddress(messageId: Long): String {
|
||||
val uri = Sms.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Sms.ADDRESS
|
||||
)
|
||||
|
||||
val selection = "${Sms._ID} = ?"
|
||||
val selectionArgs = arrayOf(messageId.toString())
|
||||
|
||||
try {
|
||||
val cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
cursor?.use {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getStringValue(Sms.ADDRESS)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
fun Context.getThreadParticipants(threadId: Long, contactsMap: HashMap<Int, SimpleContact>?): ArrayList<SimpleContact> {
|
||||
val uri = Uri.parse("${MmsSms.CONTENT_CONVERSATIONS_URI}?simple=true")
|
||||
val projection = arrayOf(
|
||||
ThreadsColumns.RECIPIENT_IDS
|
||||
|
@ -335,9 +365,9 @@ fun Context.getThreadParticipants(threadId: Int, contactsMap: HashMap<Int, Simpl
|
|||
|
||||
val phoneNumber = getPhoneNumberFromAddressId(addressId)
|
||||
val namePhoto = getNameAndPhotoFromPhoneNumber(phoneNumber)
|
||||
val name = namePhoto?.name ?: ""
|
||||
val photoUri = namePhoto?.photoUri ?: ""
|
||||
val contact = SimpleContact(addressId, addressId, name, photoUri, arrayListOf(phoneNumber))
|
||||
val name = namePhoto.name
|
||||
val photoUri = namePhoto.photoUri ?: ""
|
||||
val contact = SimpleContact(addressId, addressId, name, photoUri, arrayListOf(phoneNumber), ArrayList(), ArrayList())
|
||||
participants.add(contact)
|
||||
}
|
||||
}
|
||||
|
@ -356,10 +386,20 @@ fun Context.getThreadPhoneNumbers(recipientIds: List<Int>): ArrayList<String> {
|
|||
return numbers
|
||||
}
|
||||
|
||||
fun Context.getThreadContactNames(phoneNumbers: List<String>): ArrayList<String> {
|
||||
fun Context.getThreadContactNames(phoneNumbers: List<String>, privateContacts: ArrayList<SimpleContact>): ArrayList<String> {
|
||||
val names = ArrayList<String>()
|
||||
phoneNumbers.forEach {
|
||||
names.add(SimpleContactsHelper(this).getNameFromPhoneNumber(it))
|
||||
phoneNumbers.forEach { number ->
|
||||
val name = SimpleContactsHelper(this).getNameFromPhoneNumber(number)
|
||||
if (name != number) {
|
||||
names.add(name)
|
||||
} else {
|
||||
val privateContact = privateContacts.firstOrNull { it.doesContainPhoneNumber(number) }
|
||||
if (privateContact == null) {
|
||||
names.add(name)
|
||||
} else {
|
||||
names.add(privateContact.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
@ -400,9 +440,9 @@ fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): Arr
|
|||
queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor ->
|
||||
val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor
|
||||
val namePhoto = getNameAndPhotoFromPhoneNumber(senderNumber)
|
||||
var senderName = namePhoto?.name ?: ""
|
||||
var photoUri = namePhoto?.photoUri ?: ""
|
||||
if (namePhoto == null || isNumberBlocked(senderNumber, blockedNumbers)) {
|
||||
var senderName = namePhoto.name
|
||||
var photoUri = namePhoto.photoUri ?: ""
|
||||
if (isNumberBlocked(senderNumber, blockedNumbers)) {
|
||||
return@queryCursor
|
||||
} else if (namePhoto.name == senderNumber) {
|
||||
if (privateContacts.isNotEmpty()) {
|
||||
|
@ -418,7 +458,7 @@ fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): Arr
|
|||
}
|
||||
}
|
||||
|
||||
val contact = SimpleContact(0, 0, senderName, photoUri, arrayListOf(senderNumber))
|
||||
val contact = SimpleContact(0, 0, senderName, photoUri, arrayListOf(senderNumber), ArrayList(), ArrayList())
|
||||
if (!contacts.map { it.phoneNumbers.first().trimToComparableNumber() }.contains(senderNumber.trimToComparableNumber())) {
|
||||
contacts.add(contact)
|
||||
}
|
||||
|
@ -427,7 +467,7 @@ fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): Arr
|
|||
return contacts
|
||||
}
|
||||
|
||||
fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto? {
|
||||
fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto {
|
||||
if (!hasPermission(PERMISSION_READ_CONTACTS)) {
|
||||
return NamePhoto(number, null)
|
||||
}
|
||||
|
@ -454,7 +494,7 @@ fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto? {
|
|||
return NamePhoto(number, null)
|
||||
}
|
||||
|
||||
fun Context.insertNewSMS(address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int, subscriptionId: Int): Int {
|
||||
fun Context.insertNewSMS(address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int, subscriptionId: Int): Long {
|
||||
val uri = Sms.CONTENT_URI
|
||||
val contentValues = ContentValues().apply {
|
||||
put(Sms.ADDRESS, address)
|
||||
|
@ -468,29 +508,39 @@ fun Context.insertNewSMS(address: String, subject: String, body: String, date: L
|
|||
}
|
||||
|
||||
val newUri = contentResolver.insert(uri, contentValues)
|
||||
return newUri?.lastPathSegment?.toInt() ?: 0
|
||||
return newUri?.lastPathSegment?.toLong() ?: 0L
|
||||
}
|
||||
|
||||
fun Context.deleteConversation(threadId: Int) {
|
||||
fun Context.deleteConversation(threadId: Long) {
|
||||
var uri = Sms.CONTENT_URI
|
||||
val selection = "${Sms.THREAD_ID} = ?"
|
||||
val selectionArgs = arrayOf(threadId.toString())
|
||||
contentResolver.delete(uri, selection, selectionArgs)
|
||||
try {
|
||||
contentResolver.delete(uri, selection, selectionArgs)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
|
||||
uri = Mms.CONTENT_URI
|
||||
contentResolver.delete(uri, selection, selectionArgs)
|
||||
|
||||
conversationsDB.deleteThreadId(threadId.toLong())
|
||||
conversationsDB.deleteThreadId(threadId)
|
||||
messagesDB.deleteThreadMessages(threadId)
|
||||
}
|
||||
|
||||
fun Context.deleteMessage(id: Int, isMMS: Boolean) {
|
||||
fun Context.deleteMessage(id: Long, isMMS: Boolean) {
|
||||
val uri = if (isMMS) Mms.CONTENT_URI else Sms.CONTENT_URI
|
||||
val selection = "${Sms._ID} = ?"
|
||||
val selectionArgs = arrayOf(id.toString())
|
||||
contentResolver.delete(uri, selection, selectionArgs)
|
||||
try {
|
||||
contentResolver.delete(uri, selection, selectionArgs)
|
||||
messagesDB.delete(id)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.markMessageRead(id: Int, isMMS: Boolean) {
|
||||
fun Context.markMessageRead(id: Long, isMMS: Boolean) {
|
||||
val uri = if (isMMS) Mms.CONTENT_URI else Sms.CONTENT_URI
|
||||
val contentValues = ContentValues().apply {
|
||||
put(Sms.READ, 1)
|
||||
|
@ -499,9 +549,10 @@ fun Context.markMessageRead(id: Int, isMMS: Boolean) {
|
|||
val selection = "${Sms._ID} = ?"
|
||||
val selectionArgs = arrayOf(id.toString())
|
||||
contentResolver.update(uri, contentValues, selection, selectionArgs)
|
||||
messagesDB.markRead(id)
|
||||
}
|
||||
|
||||
fun Context.markThreadMessagesRead(threadId: Int) {
|
||||
fun Context.markThreadMessagesRead(threadId: Long) {
|
||||
arrayOf(Sms.CONTENT_URI, Mms.CONTENT_URI).forEach { uri ->
|
||||
val contentValues = ContentValues().apply {
|
||||
put(Sms.READ, 1)
|
||||
|
@ -511,9 +562,10 @@ fun Context.markThreadMessagesRead(threadId: Int) {
|
|||
val selectionArgs = arrayOf(threadId.toString())
|
||||
contentResolver.update(uri, contentValues, selection, selectionArgs)
|
||||
}
|
||||
messagesDB.markThreadRead(threadId)
|
||||
}
|
||||
|
||||
fun Context.markThreadMessagesUnread(threadId: Int) {
|
||||
fun Context.markThreadMessagesUnread(threadId: Long) {
|
||||
arrayOf(Sms.CONTENT_URI, Mms.CONTENT_URI).forEach { uri ->
|
||||
val contentValues = ContentValues().apply {
|
||||
put(Sms.READ, 0)
|
||||
|
@ -525,6 +577,26 @@ fun Context.markThreadMessagesUnread(threadId: Int) {
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.updateMessageType(id: Long, status: Int) {
|
||||
val uri = Sms.CONTENT_URI
|
||||
val contentValues = ContentValues().apply {
|
||||
put(Sms.TYPE, status)
|
||||
}
|
||||
val selection = "${Sms._ID} = ?"
|
||||
val selectionArgs = arrayOf(id.toString())
|
||||
contentResolver.update(uri, contentValues, selection, selectionArgs)
|
||||
}
|
||||
|
||||
fun Context.updateMessageSubscriptionId(messageId: Long, subscriptionId: Int) {
|
||||
val uri = Sms.CONTENT_URI
|
||||
val contentValues = ContentValues().apply {
|
||||
put(Sms.SUBSCRIPTION_ID, subscriptionId)
|
||||
}
|
||||
val selection = "${Sms._ID} = ?"
|
||||
val selectionArgs = arrayOf(messageId.toString())
|
||||
contentResolver.update(uri, contentValues, selection, selectionArgs)
|
||||
}
|
||||
|
||||
fun Context.updateUnreadCountBadge(conversations: List<Conversation>) {
|
||||
val unreadCount = conversations.count { !it.read }
|
||||
if (unreadCount == 0) {
|
||||
|
@ -560,23 +632,28 @@ fun Context.getThreadId(addresses: Set<String>): Long {
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.showReceivedMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?) {
|
||||
val privateCursor = getMyContactsCursor().loadInBackground()
|
||||
fun Context.showReceivedMessageNotification(address: String, body: String, threadId: Long, bitmap: Bitmap?) {
|
||||
val privateCursor = getMyContactsCursor()?.loadInBackground()
|
||||
ensureBackgroundThread {
|
||||
var sender = getNameAndPhotoFromPhoneNumber(address)?.name ?: ""
|
||||
if (address == sender) {
|
||||
val privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
sender = privateContacts.firstOrNull { it.doesContainPhoneNumber(address) }?.name ?: address
|
||||
}
|
||||
val senderName = getNameFromAddress(address, privateCursor)
|
||||
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
showMessageNotification(address, body, threadID, bitmap, sender)
|
||||
showMessageNotification(address, body, threadId, bitmap, senderName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getNameFromAddress(address: String, privateCursor: Cursor?): String {
|
||||
var sender = getNameAndPhotoFromPhoneNumber(address).name
|
||||
if (address == sender) {
|
||||
val privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
sender = privateContacts.firstOrNull { it.doesContainPhoneNumber(address) }?.name ?: address
|
||||
}
|
||||
return sender
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun Context.showMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?, sender: String) {
|
||||
fun Context.showMessageNotification(address: String, body: String, threadId: Long, bitmap: Bitmap?, sender: String) {
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
||||
if (isOreoPlus()) {
|
||||
|
@ -598,14 +675,14 @@ fun Context.showMessageNotification(address: String, body: String, threadID: Int
|
|||
}
|
||||
|
||||
val intent = Intent(this, ThreadActivity::class.java).apply {
|
||||
putExtra(THREAD_ID, threadID)
|
||||
putExtra(THREAD_ID, threadId)
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(this, threadID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pendingIntent = PendingIntent.getActivity(this, threadId.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val summaryText = getString(R.string.new_message)
|
||||
val markAsReadIntent = Intent(this, MarkAsReadReceiver::class.java).apply {
|
||||
action = MARK_AS_READ
|
||||
putExtra(THREAD_ID, threadID)
|
||||
putExtra(THREAD_ID, threadId)
|
||||
}
|
||||
|
||||
val markAsReadPendingIntent = PendingIntent.getBroadcast(this, 0, markAsReadIntent, PendingIntent.FLAG_CANCEL_CURRENT)
|
||||
|
@ -618,11 +695,11 @@ fun Context.showMessageNotification(address: String, body: String, threadID: Int
|
|||
.build()
|
||||
|
||||
val replyIntent = Intent(this, DirectReplyReceiver::class.java).apply {
|
||||
putExtra(THREAD_ID, threadID)
|
||||
putExtra(THREAD_ID, threadId)
|
||||
putExtra(THREAD_NUMBER, address)
|
||||
}
|
||||
|
||||
val replyPendingIntent = PendingIntent.getBroadcast(applicationContext, threadID, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val replyPendingIntent = PendingIntent.getBroadcast(applicationContext, threadId.hashCode(), replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
replyAction = NotificationCompat.Action.Builder(R.drawable.ic_send_vector, replyLabel, replyPendingIntent)
|
||||
.addRemoteInput(remoteInput)
|
||||
.build()
|
||||
|
@ -632,7 +709,7 @@ fun Context.showMessageNotification(address: String, body: String, threadID: Int
|
|||
val builder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL)
|
||||
.setContentTitle(sender)
|
||||
.setContentText(body)
|
||||
.setColor(config.primaryColor)
|
||||
.setColor(getAdjustedPrimaryColor())
|
||||
.setSmallIcon(R.drawable.ic_messenger)
|
||||
.setLargeIcon(largeIcon)
|
||||
.setStyle(NotificationCompat.BigTextStyle().setSummaryText(summaryText).bigText(body))
|
||||
|
@ -648,5 +725,5 @@ fun Context.showMessageNotification(address: String, body: String, threadID: Int
|
|||
builder.addAction(R.drawable.ic_check_vector, getString(R.string.mark_as_read), markAsReadPendingIntent)
|
||||
.setChannelId(NOTIFICATION_CHANNEL)
|
||||
|
||||
notificationManager.notify(threadID, builder.build())
|
||||
notificationManager.notify(threadId.hashCode(), builder.build())
|
||||
}
|
||||
|
|
|
@ -13,4 +13,8 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
}
|
||||
|
||||
fun getUseSIMIdAtNumber(number: String) = prefs.getInt(USE_SIM_ID_PREFIX + number, 0)
|
||||
|
||||
var showCharacterCounter: Boolean
|
||||
get() = prefs.getBoolean(SHOW_CHARACTER_COUNTER, false)
|
||||
set(showCharacterCounter) = prefs.edit().putBoolean(SHOW_CHARACTER_COUNTER, showCharacterCounter).apply()
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ const val THREAD_TEXT = "thread_text"
|
|||
const val THREAD_NUMBER = "thread_number"
|
||||
const val THREAD_ATTACHMENT_URI = "thread_attachment_uri"
|
||||
const val THREAD_ATTACHMENT_URIS = "thread_attachment_uris"
|
||||
const val SEARCHED_MESSAGE_ID = "searched_message_id"
|
||||
const val USE_SIM_ID_PREFIX = "use_sim_id_"
|
||||
const val NOTIFICATION_CHANNEL = "simple_sms_messenger"
|
||||
const val SHOW_CHARACTER_COUNTER = "show_character_counter"
|
||||
|
||||
private const val PATH = "com.simplemobiletools.smsmessenger.action."
|
||||
const val MARK_AS_READ = PATH + "mark_as_read"
|
||||
|
@ -22,6 +24,7 @@ const val THREAD_RECEIVED_MESSAGE = 2
|
|||
const val THREAD_SENT_MESSAGE = 3
|
||||
const val THREAD_SENT_MESSAGE_ERROR = 4
|
||||
const val THREAD_SENT_MESSAGE_SUCCESS = 5
|
||||
const val THREAD_SENT_MESSAGE_SENDING = 6
|
||||
|
||||
fun refreshMessages() {
|
||||
EventBus.getDefault().post(Events.RefreshMessages())
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.simplemobiletools.commons.models.SimpleContact
|
||||
import com.simplemobiletools.smsmessenger.models.Attachment
|
||||
import com.simplemobiletools.smsmessenger.models.MessageAttachment
|
||||
|
||||
class Converters {
|
||||
private val gson = Gson()
|
||||
private val attachmentType = object : TypeToken<List<Attachment>>() {}.type
|
||||
private val simpleContactType = object : TypeToken<List<SimpleContact>>() {}.type
|
||||
private val messageAttachmentType = object : TypeToken<MessageAttachment?>() {}.type
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToAttachmentList(value: String) = gson.fromJson<ArrayList<Attachment>>(value, attachmentType)
|
||||
|
||||
@TypeConverter
|
||||
fun attachmentListToJson(list: ArrayList<Attachment>) = gson.toJson(list)
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToSimpleContactList(value: String) = gson.fromJson<ArrayList<SimpleContact>>(value, simpleContactType)
|
||||
|
||||
@TypeConverter
|
||||
fun simpleContactListToJson(list: ArrayList<SimpleContact>) = gson.toJson(list)
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToMessageAttachment(value: String) = gson.fromJson<MessageAttachment>(value, messageAttachmentType)
|
||||
|
||||
@TypeConverter
|
||||
fun messageAttachmentToJson(messageAttachment: MessageAttachment?) = gson.toJson(messageAttachment)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.simplemobiletools.smsmessenger.interfaces
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.simplemobiletools.smsmessenger.models.Attachment
|
||||
|
||||
@Dao
|
||||
interface AttachmentsDao {
|
||||
@Query("SELECT * FROM attachments")
|
||||
fun getAll(): List<Attachment>
|
||||
}
|
|
@ -17,15 +17,15 @@ interface ConversationsDao {
|
|||
@Query("SELECT * FROM conversations WHERE read = 0")
|
||||
fun getUnreadConversations(): List<Conversation>
|
||||
|
||||
@Query("SELECT * FROM conversations WHERE title LIKE :text")
|
||||
fun getConversationsWithText(text: String): List<Conversation>
|
||||
|
||||
@Query("UPDATE conversations SET read = 1 WHERE thread_id = :threadId")
|
||||
fun markRead(threadId: Long)
|
||||
|
||||
@Query("UPDATE conversations SET read = 0 WHERE thread_id = :threadId")
|
||||
fun markUnread(threadId: Long)
|
||||
|
||||
@Query("DELETE FROM conversations WHERE id = :id")
|
||||
fun delete(id: Long)
|
||||
|
||||
@Query("DELETE FROM conversations WHERE thread_id = :threadId")
|
||||
fun deleteThreadId(threadId: Long)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.simplemobiletools.smsmessenger.interfaces
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.simplemobiletools.smsmessenger.models.MessageAttachment
|
||||
|
||||
@Dao
|
||||
interface MessageAttachmentsDao {
|
||||
@Query("SELECT * FROM message_attachments")
|
||||
fun getAll(): List<MessageAttachment>
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.simplemobiletools.smsmessenger.interfaces
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.simplemobiletools.smsmessenger.models.Message
|
||||
|
||||
@Dao
|
||||
interface MessagesDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOrUpdate(message: Message)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
fun insertOrIgnore(message: Message): Long
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertMessages(vararg message: Message)
|
||||
|
||||
@Query("SELECT * FROM messages")
|
||||
fun getAll(): List<Message>
|
||||
|
||||
@Query("SELECT * FROM messages WHERE thread_id = :threadId")
|
||||
fun getThreadMessages(threadId: Long): List<Message>
|
||||
|
||||
@Query("SELECT * FROM messages WHERE body LIKE :text")
|
||||
fun getMessagesWithText(text: String): List<Message>
|
||||
|
||||
@Query("UPDATE messages SET read = 1 WHERE id = :id")
|
||||
fun markRead(id: Long)
|
||||
|
||||
@Query("UPDATE messages SET read = 1 WHERE thread_id = :threadId")
|
||||
fun markThreadRead(threadId: Long)
|
||||
|
||||
@Query("UPDATE messages SET type = :type WHERE id = :id")
|
||||
fun updateType(id: Long, type: Int): Int
|
||||
|
||||
@Query("DELETE FROM messages WHERE id = :id")
|
||||
fun delete(id: Long)
|
||||
|
||||
@Query("DELETE FROM messages WHERE thread_id = :threadId")
|
||||
fun deleteThreadMessages(threadId: Long)
|
||||
}
|
|
@ -1,5 +1,20 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
data class Attachment(var uri: Uri, var mimetype: String, var width: Int, var height: Int, var filename: String)
|
||||
@Entity(tableName = "attachments", indices = [(Index(value = ["message_id"], unique = true))])
|
||||
data class Attachment(
|
||||
@PrimaryKey(autoGenerate = true) var id: Long?,
|
||||
@ColumnInfo(name = "message_id") var messageId: Long,
|
||||
@ColumnInfo(name = "uri_string") var uriString: String,
|
||||
@ColumnInfo(name = "mimetype") var mimetype: String,
|
||||
@ColumnInfo(name = "width") var width: Int,
|
||||
@ColumnInfo(name = "height") var height: Int,
|
||||
@ColumnInfo(name = "filename") var filename: String) {
|
||||
|
||||
fun getUri() = Uri.parse(uriString)
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ import androidx.room.PrimaryKey
|
|||
|
||||
@Entity(tableName = "conversations", indices = [(Index(value = ["thread_id"], unique = true))])
|
||||
data class Conversation(
|
||||
@PrimaryKey(autoGenerate = true) var id: Long?,
|
||||
@ColumnInfo(name = "thread_id") var thread_id: Int,
|
||||
@PrimaryKey @ColumnInfo(name = "thread_id") var threadId: Long,
|
||||
@ColumnInfo(name = "snippet") var snippet: String,
|
||||
@ColumnInfo(name = "date") var date: Int,
|
||||
@ColumnInfo(name = "read") var read: Boolean,
|
||||
|
@ -16,8 +15,4 @@ data class Conversation(
|
|||
@ColumnInfo(name = "photo_uri") var photoUri: String,
|
||||
@ColumnInfo(name = "is_group_conversation") var isGroupConversation: Boolean,
|
||||
@ColumnInfo(name = "phone_number") var phoneNumber: String
|
||||
) {
|
||||
fun getStringToCompare(): String {
|
||||
return copy(id = 0).toString()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
import android.provider.Telephony
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.simplemobiletools.commons.models.SimpleContact
|
||||
|
||||
@Entity(tableName = "messages")
|
||||
data class Message(
|
||||
val id: Int, val body: String, val type: Int, val participants: ArrayList<SimpleContact>, val date: Int, val read: Boolean, val thread: Int,
|
||||
val isMMS: Boolean, val attachment: MessageAttachment?, var senderName: String, val senderPhotoUri: String, val subscriptionId: Int) : ThreadItem() {
|
||||
@PrimaryKey val id: Long,
|
||||
@ColumnInfo(name = "body") val body: String,
|
||||
@ColumnInfo(name = "type") val type: Int,
|
||||
@ColumnInfo(name = "participants") val participants: ArrayList<SimpleContact>,
|
||||
@ColumnInfo(name = "date") val date: Int,
|
||||
@ColumnInfo(name = "read") val read: Boolean,
|
||||
@ColumnInfo(name = "thread_id") val threadId: Long,
|
||||
@ColumnInfo(name = "is_mms") val isMMS: Boolean,
|
||||
@ColumnInfo(name = "attachment") val attachment: MessageAttachment?,
|
||||
@ColumnInfo(name = "sender_name") var senderName: String,
|
||||
@ColumnInfo(name = "sender_photo_uri") val senderPhotoUri: String,
|
||||
@ColumnInfo(name = "subscription_id") var subscriptionId: Int) : ThreadItem() {
|
||||
|
||||
fun isReceivedMessage() = type == Telephony.Sms.MESSAGE_TYPE_INBOX
|
||||
}
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
data class MessageAttachment(val id: Int, var text: String, var attachments: ArrayList<Attachment>)
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "message_attachments")
|
||||
data class MessageAttachment(
|
||||
@PrimaryKey val id: Long,
|
||||
@ColumnInfo(name = "text") var text: String,
|
||||
@ColumnInfo(name = "attachments") var attachments: ArrayList<Attachment>)
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
data class SearchResult(val messageId: Long, val title: String, val snippet: String, val date: String, val threadId: Long, var photoUri: String)
|
|
@ -1,3 +1,3 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
data class ThreadError(val messageID: Int) : ThreadItem()
|
||||
data class ThreadError(val messageId: Long, val messageText: String) : ThreadItem()
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
data class ThreadSending(val messageId: Long) : ThreadItem()
|
|
@ -1,4 +1,4 @@
|
|||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
// show a check after the latest message, if it is a sent one and succeeded
|
||||
data class ThreadSuccess(val messageID: Int) : ThreadItem()
|
||||
data class ThreadSuccess(val messageID: Long) : ThreadItem()
|
||||
|
|
|
@ -21,7 +21,7 @@ import com.simplemobiletools.smsmessenger.helpers.THREAD_NUMBER
|
|||
class DirectReplyReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val address = intent.getStringExtra(THREAD_NUMBER)
|
||||
val threadId = intent.getIntExtra(THREAD_ID, 0)
|
||||
val threadId = intent.getLongExtra(THREAD_ID, 0L)
|
||||
val msg = RemoteInput.getResultsFromIntent(intent).getCharSequence(REPLY).toString()
|
||||
|
||||
val settings = Settings()
|
||||
|
@ -31,7 +31,7 @@ class DirectReplyReceiver : BroadcastReceiver() {
|
|||
val message = com.klinker.android.send_message.Message(msg, address)
|
||||
|
||||
try {
|
||||
transaction.sendNewMessage(message, threadId.toLong())
|
||||
transaction.sendNewMessage(message, threadId)
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
}
|
||||
|
@ -41,11 +41,11 @@ class DirectReplyReceiver : BroadcastReceiver() {
|
|||
.setContentText(msg)
|
||||
.build()
|
||||
|
||||
context.notificationManager.notify(threadId, repliedNotification)
|
||||
context.notificationManager.notify(threadId.hashCode(), repliedNotification)
|
||||
|
||||
ensureBackgroundThread {
|
||||
context.markThreadMessagesRead(threadId)
|
||||
context.conversationsDB.markRead(threadId.toLong())
|
||||
context.conversationsDB.markRead(threadId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ class MarkAsReadReceiver : BroadcastReceiver() {
|
|||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
MARK_AS_READ -> {
|
||||
val threadId = intent.getIntExtra(THREAD_ID, 0)
|
||||
context.notificationManager.cancel(threadId)
|
||||
val threadId = intent.getLongExtra(THREAD_ID, 0L)
|
||||
context.notificationManager.cancel(threadId.hashCode())
|
||||
ensureBackgroundThread {
|
||||
context.markThreadMessagesRead(threadId)
|
||||
context.conversationsDB.markRead(threadId.toLong())
|
||||
context.conversationsDB.markRead(threadId)
|
||||
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() {
|
|||
val glideBitmap = try {
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(mms.attachment!!.attachments.first().uri)
|
||||
.load(mms.attachment!!.attachments.first().getUri())
|
||||
.centerCrop()
|
||||
.into(size, size)
|
||||
.get()
|
||||
|
@ -33,8 +33,8 @@ class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() {
|
|||
}
|
||||
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
context.showReceivedMessageNotification(address, mms.body, mms.thread, glideBitmap)
|
||||
val conversation = context.getConversations(mms.thread.toLong()).firstOrNull() ?: return@post
|
||||
context.showReceivedMessageNotification(address, mms.body, mms.threadId, glideBitmap)
|
||||
val conversation = context.getConversations(mms.threadId).firstOrNull() ?: return@post
|
||||
ensureBackgroundThread {
|
||||
context.conversationsDB.insertOrUpdate(conversation)
|
||||
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
|
||||
|
|
|
@ -3,11 +3,15 @@ package com.simplemobiletools.smsmessenger.receivers
|
|||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Telephony
|
||||
import com.simplemobiletools.commons.extensions.isNumberBlocked
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.models.SimpleContact
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||
import com.simplemobiletools.smsmessenger.models.Message
|
||||
|
||||
class SmsReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
|
@ -21,23 +25,33 @@ class SmsReceiver : BroadcastReceiver() {
|
|||
val read = 0
|
||||
val subscriptionId = intent.getIntExtra("subscription", -1)
|
||||
|
||||
messages.forEach {
|
||||
address = it.originatingAddress ?: ""
|
||||
subject = it.pseudoSubject
|
||||
body += it.messageBody
|
||||
date = Math.min(it.timestampMillis, System.currentTimeMillis())
|
||||
threadId = context.getThreadId(address)
|
||||
}
|
||||
ensureBackgroundThread {
|
||||
messages.forEach {
|
||||
address = it.originatingAddress ?: ""
|
||||
subject = it.pseudoSubject
|
||||
body += it.messageBody
|
||||
date = Math.min(it.timestampMillis, System.currentTimeMillis())
|
||||
threadId = context.getThreadId(address)
|
||||
}
|
||||
|
||||
if (!context.isNumberBlocked(address)) {
|
||||
context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
|
||||
context.showReceivedMessageNotification(address, body, threadId.toInt(), null)
|
||||
refreshMessages()
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
if (!context.isNumberBlocked(address)) {
|
||||
ensureBackgroundThread {
|
||||
val newMessageId = context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
|
||||
|
||||
ensureBackgroundThread {
|
||||
val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread
|
||||
context.conversationsDB.insertOrUpdate(conversation)
|
||||
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
|
||||
val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread
|
||||
context.conversationsDB.insertOrUpdate(conversation)
|
||||
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
|
||||
|
||||
val participant = SimpleContact(0, 0, address, "", arrayListOf(address), ArrayList(), ArrayList())
|
||||
val message = Message(newMessageId, body, type, arrayListOf(participant), (date / 1000).toInt(), false, threadId,
|
||||
false, null, address, "", subscriptionId)
|
||||
context.messagesDB.insertOrUpdate(message)
|
||||
}
|
||||
|
||||
context.showReceivedMessageNotification(address, body, threadId, null)
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package com.simplemobiletools.smsmessenger.receivers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.klinker.android.send_message.SentReceiver
|
||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||
|
||||
class SmsSentReceiver : SentReceiver() {
|
||||
override fun onMessageStatusUpdated(context: Context, intent: Intent, receiverResultCode: Int) {
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.simplemobiletools.smsmessenger.receivers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Telephony
|
||||
import com.klinker.android.send_message.DeliveredReceiver
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.smsmessenger.extensions.messagesDB
|
||||
import com.simplemobiletools.smsmessenger.extensions.updateMessageType
|
||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||
|
||||
class SmsStatusDeliveredReceiver : DeliveredReceiver() {
|
||||
|
||||
override fun onMessageStatusUpdated(context: Context, intent: Intent, receiverResultCode: Int) {
|
||||
if (intent.extras?.containsKey("message_uri") == true) {
|
||||
val uri = Uri.parse(intent.getStringExtra("message_uri"))
|
||||
val messageId = uri?.lastPathSegment?.toLong() ?: 0L
|
||||
ensureBackgroundThread {
|
||||
val type = Telephony.Sms.MESSAGE_TYPE_SENT
|
||||
context.updateMessageType(messageId, type)
|
||||
val updated = context.messagesDB.updateType(messageId, type)
|
||||
if (updated == 0) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
ensureBackgroundThread {
|
||||
context.messagesDB.updateType(messageId, type)
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.simplemobiletools.smsmessenger.receivers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioManager
|
||||
import android.media.RingtoneManager
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Telephony
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.klinker.android.send_message.SentReceiver
|
||||
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
|
||||
import com.simplemobiletools.commons.extensions.getMyContactsCursor
|
||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.helpers.isOreoPlus
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.NOTIFICATION_CHANNEL
|
||||
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
|
||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||
|
||||
class SmsStatusSentReceiver : SentReceiver() {
|
||||
|
||||
override fun onMessageStatusUpdated(context: Context, intent: Intent, receiverResultCode: Int) {
|
||||
if (intent.extras?.containsKey("message_uri") == true) {
|
||||
val uri = Uri.parse(intent.getStringExtra("message_uri"))
|
||||
val messageId = uri?.lastPathSegment?.toLong() ?: 0L
|
||||
ensureBackgroundThread {
|
||||
val type = if (intent.extras!!.containsKey("errorCode")) {
|
||||
showSendingFailedNotification(context, messageId)
|
||||
Telephony.Sms.MESSAGE_TYPE_FAILED
|
||||
} else {
|
||||
Telephony.Sms.MESSAGE_TYPE_OUTBOX
|
||||
}
|
||||
context.updateMessageType(messageId, type)
|
||||
context.messagesDB.updateType(messageId, type)
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSendingFailedNotification(context: Context, messageId: Long) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
val privateCursor = context.getMyContactsCursor()?.loadInBackground()
|
||||
ensureBackgroundThread {
|
||||
val address = context.getMessageRecipientAddress(messageId)
|
||||
val threadId = context.getThreadId(address)
|
||||
val senderName = context.getNameFromAddress(address, privateCursor)
|
||||
showNotification(context, senderName, threadId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun showNotification(context: Context, recipientName: String, threadId: Long) {
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
||||
if (isOreoPlus()) {
|
||||
val audioAttributes = AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
|
||||
.build()
|
||||
|
||||
val name = context.getString(R.string.message_not_sent_short)
|
||||
val importance = NotificationManager.IMPORTANCE_HIGH
|
||||
NotificationChannel(NOTIFICATION_CHANNEL, name, importance).apply {
|
||||
setBypassDnd(false)
|
||||
enableLights(true)
|
||||
setSound(soundUri, audioAttributes)
|
||||
enableVibration(true)
|
||||
notificationManager.createNotificationChannel(this)
|
||||
}
|
||||
}
|
||||
|
||||
val intent = Intent(context, ThreadActivity::class.java).apply {
|
||||
putExtra(THREAD_ID, threadId)
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(context, threadId.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val summaryText = String.format(context.getString(R.string.message_sending_error), recipientName)
|
||||
|
||||
val largeIcon = SimpleContactsHelper(context).getContactLetterIcon(recipientName)
|
||||
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
|
||||
.setContentTitle(context.getString(R.string.message_not_sent_short))
|
||||
.setContentText(summaryText)
|
||||
.setColor(context.getAdjustedPrimaryColor())
|
||||
.setSmallIcon(R.drawable.ic_messenger)
|
||||
.setLargeIcon(largeIcon)
|
||||
.setStyle(NotificationCompat.BigTextStyle().bigText(summaryText))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(Notification.DEFAULT_LIGHTS)
|
||||
.setCategory(Notification.CATEGORY_MESSAGE)
|
||||
.setAutoCancel(true)
|
||||
.setSound(soundUri, AudioManager.STREAM_NOTIFICATION)
|
||||
.setChannelId(NOTIFICATION_CHANNEL)
|
||||
|
||||
notificationManager.notify(threadId.hashCode(), builder.build())
|
||||
}
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/selected_contact_bg">
|
||||
<shape android:shape="rectangle">
|
||||
|
||||
<corners android:radius="@dimen/normal_margin" />
|
||||
<corners android:radius="@dimen/normal_margin" />
|
||||
|
||||
<solid android:color="@color/activated_item_foreground" />
|
||||
<solid android:color="@color/md_grey_white" />
|
||||
|
||||
</shape>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
android:id="@+id/no_contacts_placeholder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/message_divider_two"
|
||||
android:layout_below="@+id/suggestions_scrollview"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/bigger_margin"
|
||||
android:alpha="0.8"
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/search_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/search_placeholder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:alpha="0.8"
|
||||
android:gravity="center"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:text="@string/no_items_found"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
android:textStyle="italic" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/search_placeholder_2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/search_placeholder"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:alpha="0.8"
|
||||
android:gravity="center"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:paddingBottom="@dimen/medium_margin"
|
||||
android:text="@string/type_2_characters"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
android:textStyle="italic" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyRecyclerView
|
||||
android:id="@+id/search_results_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -53,6 +53,27 @@
|
|||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/settings_customize_notifications_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingLeft="@dimen/normal_margin"
|
||||
android:paddingTop="@dimen/activity_margin"
|
||||
android:paddingRight="@dimen/normal_margin"
|
||||
android:paddingBottom="@dimen/activity_margin">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/settings_customize_notifications_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:paddingStart="@dimen/medium_margin"
|
||||
android:text="@string/customize_notifications" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/settings_manage_blocked_numbers_holder"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -149,5 +170,28 @@
|
|||
android:clickable="false" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/settings_show_character_counter_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingStart="@dimen/normal_margin"
|
||||
android:paddingTop="@dimen/activity_margin"
|
||||
android:paddingEnd="@dimen/normal_margin"
|
||||
android:paddingBottom="@dimen/activity_margin">
|
||||
|
||||
<com.simplemobiletools.commons.views.MySwitchCompat
|
||||
android:id="@+id/settings_show_character_counter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:paddingStart="@dimen/medium_margin"
|
||||
android:text="@string/show_character_counter"
|
||||
app:switchPadding="@dimen/medium_margin" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
android:layout_marginStart="@dimen/medium_margin"
|
||||
android:layout_marginEnd="@dimen/medium_margin"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/confirm_selection"
|
||||
android:paddingStart="@dimen/medium_margin"
|
||||
android:paddingEnd="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_check_vector"
|
||||
|
@ -125,6 +126,7 @@
|
|||
android:layout_marginEnd="@dimen/small_margin"
|
||||
android:alpha="0.9"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/attachment"
|
||||
android:padding="@dimen/normal_margin"
|
||||
android:src="@drawable/ic_plus_vector" />
|
||||
|
||||
|
@ -166,14 +168,17 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/thread_select_sim_icon"
|
||||
android:layout_width="@dimen/normal_icon_size"
|
||||
android:layout_height="@dimen/normal_icon_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="@dimen/small_margin"
|
||||
android:layout_toStartOf="@+id/thread_send_message"
|
||||
android:layout_toStartOf="@+id/thread_character_counter"
|
||||
android:alpha="0.9"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:padding="@dimen/normal_margin"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:paddingStart="@dimen/medium_margin"
|
||||
android:paddingTop="@dimen/normal_margin"
|
||||
android:paddingEnd="@dimen/medium_margin"
|
||||
android:paddingBottom="@dimen/normal_margin"
|
||||
android:src="@drawable/ic_sim_vector"
|
||||
android:visibility="gone" />
|
||||
|
||||
|
@ -190,6 +195,21 @@
|
|||
android:visibility="gone"
|
||||
tools:text="1" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/thread_character_counter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignTop="@+id/thread_send_message"
|
||||
android:layout_alignBottom="@+id/thread_send_message"
|
||||
android:layout_toStartOf="@+id/thread_send_message"
|
||||
android:gravity="center"
|
||||
android:paddingStart="@dimen/small_margin"
|
||||
android:paddingEnd="@dimen/small_margin"
|
||||
android:text="0"
|
||||
android:textSize="@dimen/normal_text_size"
|
||||
android:visibility="gone"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thread_send_message"
|
||||
android:layout_width="@dimen/normal_icon_size"
|
||||
|
@ -201,6 +221,7 @@
|
|||
android:alpha="0.4"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:clickable="false"
|
||||
android:contentDescription="@string/ok"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_send_vector" />
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.simplemobiletools.commons.views.MyTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/dialog_select_text_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/big_margin"
|
||||
android:paddingTop="@dimen/big_margin"
|
||||
android:paddingEnd="@dimen/big_margin"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/big_text_size"
|
||||
tools:text="My sample text" />
|
|
@ -18,7 +18,7 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_percent="0.7">
|
||||
app:layout_constraintWidth_percent="0.8">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thread_message_sender_photo"
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<?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/search_result_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/small_margin"
|
||||
android:layout_marginBottom="@dimen/small_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="@dimen/normal_margin">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/search_result_image"
|
||||
android:layout_width="@dimen/normal_icon_size"
|
||||
android:layout_height="@dimen/normal_icon_size"
|
||||
android:layout_marginEnd="@dimen/normal_margin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/search_result_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toStartOf="@+id/search_result_date"
|
||||
android:layout_toEndOf="@+id/search_result_image"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:textSize="@dimen/big_text_size"
|
||||
tools:text="John" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/search_result_snippet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/search_result_title"
|
||||
android:layout_toEndOf="@+id/search_result_image"
|
||||
android:alpha="0.7"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:textSize="@dimen/big_text_size"
|
||||
tools:text="Hey buddy!" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/search_result_date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignTop="@+id/search_result_title"
|
||||
android:layout_alignBottom="@+id/search_result_title"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginTop="@dimen/tiny_margin"
|
||||
android:alpha="0.7"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="@dimen/smaller_text_size"
|
||||
tools:text="08/02/2021" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -18,7 +18,7 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_percent="0.7">
|
||||
app:layout_constraintWidth_percent="0.8">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/thread_mesage_attachments_holder"
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/suggested_contact_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/suggested_contact_image"
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
android:gravity="end"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:text="@string/message_not_sent"
|
||||
android:text="@string/message_not_sent_touch_retry"
|
||||
android:textColor="@color/theme_dark_red_primary_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/thread_sending"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:text="@string/sending"
|
||||
android:textSize="@dimen/normal_text_size" />
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/cab_delete"
|
||||
android:icon="@drawable/ic_delete_vector"
|
||||
android:title="@string/delete"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/cab_add_number_to_contact"
|
||||
android:icon="@drawable/ic_add_person_vector"
|
||||
|
@ -12,13 +17,16 @@
|
|||
android:title="@string/block_number"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/cab_select_all"
|
||||
android:icon="@drawable/ic_select_all_vector"
|
||||
android:title="@string/select_all"
|
||||
android:id="@+id/cab_dial_number"
|
||||
android:icon="@drawable/ic_phone_vector"
|
||||
android:title="@string/dial_number"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/cab_delete"
|
||||
android:icon="@drawable/ic_delete_vector"
|
||||
android:title="@string/delete"
|
||||
app:showAsAction="ifRoom" />
|
||||
android:id="@+id/cab_copy_number"
|
||||
android:title="@string/copy_number_to_clipboard"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/cab_select_all"
|
||||
android:title="@string/select_all"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
|
|
@ -12,13 +12,16 @@
|
|||
android:title="@string/share"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/cab_select_all"
|
||||
android:icon="@drawable/ic_select_all_vector"
|
||||
android:title="@string/select_all"
|
||||
app:showAsAction="ifRoom" />
|
||||
android:id="@+id/cab_select_text"
|
||||
android:title="@string/select_text"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/cab_delete"
|
||||
android:icon="@drawable/ic_delete_vector"
|
||||
android:title="@string/delete"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/cab_select_all"
|
||||
android:title="@string/select_all"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/search"
|
||||
android:icon="@drawable/ic_search_vector"
|
||||
android:title="@string/search"
|
||||
app:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/settings"
|
||||
android:title="@string/settings"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/search"
|
||||
android:icon="@drawable/ic_search_vector"
|
||||
android:title="@string/search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="collapseActionView|always" />
|
||||
</menu>
|
|
@ -0,0 +1,82 @@
|
|||
<resources>
|
||||
<string name="app_name">Enkel SMS Beskeder</string>
|
||||
<string name="app_launcher_name">Beskeder</string>
|
||||
<string name="type_a_message">Skriv en besked…</string>
|
||||
<string name="message_not_sent_short">Beskeden blev ikke sendt</string>
|
||||
<string name="message_not_sent_touch_retry">Blev ikke sendt, tryk for at gensende</string>
|
||||
<string name="message_sending_error">Din besked til \'%s\' blev ikke sendt</string>
|
||||
<string name="add_person">Tilføj Person</string>
|
||||
<string name="attachment">Vedhæftning</string>
|
||||
<string name="no_conversations_found">Ingen gemte samtaler er fundet</string>
|
||||
<string name="start_conversation">Start en samtale</string>
|
||||
<string name="reply">Svar</string>
|
||||
<string name="show_character_counter">Vis en karaktertæller ved skrivning af beskeder</string>
|
||||
<string name="loading_messages">Henter beskeder…</string>
|
||||
<string name="no_reply_support">Afsender understøtter ikke svar</string>
|
||||
<string name="draft">Udkast</string>
|
||||
<string name="sending">Sender…</string>
|
||||
<string name="export_messages">Eksporter beskeder</string>
|
||||
<string name="import_messages">Importer beskeder</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Ny Samtale</string>
|
||||
<string name="add_contact_or_number">Tilføj kontakt eller nummer…</string>
|
||||
<string name="suggestions">Forslag</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Modtag SMS</string>
|
||||
<string name="new_message">Ny Besked</string>
|
||||
<string name="mark_as_read">Marker som læst</string>
|
||||
<string name="mark_as_unread">Marker som ulæst</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Er du sikker på, at du vil slette alle beskeder i denne samtale?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d samtale</item>
|
||||
<item quantity="other">%d samtaler</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d besked</item>
|
||||
<item quantity="other">%d beskeder</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Hvorfor kræver appen adgang til internettet?</string>
|
||||
<string name="faq_1_text">Desværre er det nødvendigt for at sende MMS-vedhæftede filer. Ikke at kunne være i stand til at sende MMS ville være en virkelig stor ulempe i forhold til andre apps, så vi besluttede at gå denne vej.
|
||||
Men som normalt er der ingen annoncer, sporing eller analyse overhovedet, internettet bruges kun til at sende 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 beskeder - Administrer dine smser nemt</string>
|
||||
<!-- Short description has to have max 80 characters -->
|
||||
<string name="app_short_description">En nem og hurtig måde at administrere SMS og MMS-beskeder uden annoncer på.</string>
|
||||
<string name="app_long_description">
|
||||
En fantastisk måde at holde kontakten med dine pårørende på, ved at sende både SMS- og MMS-beskeder. Appen håndterer også gruppemeddelelser korrekt, ligesom at blokere numre fra Android 7+.
|
||||
|
||||
Appen tilbyder mange datoformater at vælge imellem, så du kan føle dig godt tilpas ved at bruge den. Du kan også skifte mellem 12 og 24 timers tidsformat.
|
||||
|
||||
Det har en virkelig lille appstørrelse sammenlignet med konkurrencen, hvilket gør det virkelig hurtigt at downloade.
|
||||
|
||||
Den leveres med materialedesign og mørkt tema som standard og giver god brugeroplevelse til nem brug. Manglen på internetadgang giver dig mere privatliv, sikkerhed og stabilitet end andre apps.
|
||||
|
||||
Indeholder ingen annoncer eller unødvendige tilladelser. Det er fuldstændig opensource, giver farver, der kan tilpasses.
|
||||
|
||||
<b>Se hele pakken med enkle værktøjer her:</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>
|
|
@ -1,13 +1,22 @@
|
|||
<resources>
|
||||
<string name="app_name">Schlichter SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS Messenger</string>
|
||||
<string name="type_a_message">schreibe eine Nachricht…</string>
|
||||
<string name="message_not_sent">Nachricht wurde nicht gesendet.</string>
|
||||
<string name="type_a_message">Schreibe eine Nachricht…</string>
|
||||
<string name="message_not_sent_short">Nachricht nicht versendet.</string>
|
||||
<string name="message_not_sent_touch_retry">Nachricht nicht versendet. Berühre, um es erneut zu versuchen.</string>
|
||||
<string name="message_sending_error">Deine Nachricht am \'%s\' wurde nicht gesendet.</string>
|
||||
<string name="add_person">Person hinzufügen</string>
|
||||
<string name="attachment">Anhang</string>
|
||||
<string name="no_conversations_found">Keine gespeicherten Chats gefunden</string>
|
||||
<string name="start_conversation">Neuen Chat beginnen</string>
|
||||
<string name="reply">Antworten</string>
|
||||
<string name="show_character_counter">Zeige einen Zeichenzähler während dem Schreiben von Nachrichten.</string>
|
||||
<string name="loading_messages">Lade Nachrichten…</string>
|
||||
<string name="no_reply_support">Der Absender unterstützt keine Antworten.</string>
|
||||
<string name="draft">Entwurf</string>
|
||||
<string name="sending">Sende…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Neuer Chat</string>
|
||||
|
@ -42,21 +51,21 @@
|
|||
|
||||
<!-- 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>
|
||||
<string name="app_title">Simple SMS Messenger-Nachrichten simple verwalten</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_short_description">Eine einfache und schnelle Art SMS & MMS ohne Werbung zu verwalten.</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+.
|
||||
In tolle Möglichkeit mit deinen Verwandten in Kontakt zu bleiben, indem sowohl SMS als auch MMS Nachrichten gesendet werden. Die App handhabt auch Gruppennachrichten gut, genauso wie das Blockieren von Nummern ab 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.
|
||||
Damit die Nutzung sich vertrauter und komfortabler anfühlt, kannst du das Datumsformat in verschiedenen Arten anpassen. Zudem kannst du zwischen 12 und 24 Stunden Format auswählen.
|
||||
|
||||
Im Vergleich zur Konkurrenz ist die Appgröße sehr klein, wodurch es sehr schnell heruntergeladen ist.
|
||||
|
||||
It has a really tiny app size compared to the competition, making it really fast to download.
|
||||
Es kommt standardmäßig mit einem material design und Dunkelmodus, was für ein großartiges Nutzererlebnis und leichte Nutzbarkeit sorgt. Da es keine Internetverbindung verwendet, bietet diese App mehr Datenschutz, Sicherheit und Stabilität als andere Apps.
|
||||
|
||||
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.
|
||||
Beinhaltet keine Werbung oder unnötige Berechtigungen. Es ist komplett opensource, und bietet anpassbare Farben.
|
||||
|
||||
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
|
||||
|
||||
<b>Check out the full suite of Simple Tools here:</b>
|
||||
<b>Entdecke alle Simple Tools hier:</b>
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
<b>Facebook:</b>
|
||||
|
|
|
@ -2,12 +2,21 @@
|
|||
<string name="app_name">Απλός SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS Messenger</string>
|
||||
<string name="type_a_message">Πληκτρολογήστε ένα μήνυμα…</string>
|
||||
<string name="message_not_sent">Το μήνυμα δεν έχει σταλεί.</string>
|
||||
<string name="message_not_sent_short">Το μήνυμα δεν εστάλη</string>
|
||||
<string name="message_not_sent_touch_retry">Δεν εστάλη. Αγγίξτε για επανάληψη.</string>
|
||||
<string name="message_sending_error">Το μήνυμά σας προς το \'%s\' δεν έχει αποσταλεί</string>
|
||||
<string name="add_person">Προσθήκη ατόμου</string>
|
||||
<string name="attachment">Συνημμένο</string>
|
||||
<string name="no_conversations_found">Δεν βρέθηκαν αποθηκευμένες συνομιλίες</string>
|
||||
<string name="start_conversation">Έναρξη συνομιλίας</string>
|
||||
<string name="reply">Απάντηση</string>
|
||||
<string name="show_character_counter">Εμφάνιση μετρητή χαρακτήρων κατά την πληκτρολόγηση μηνυμάτων</string>
|
||||
<string name="loading_messages">Φόρτωση μηνυμάτων…</string>
|
||||
<string name="no_reply_support">Ο αποστολέας δεν υποστηρίζει απαντήσεις</string>
|
||||
<string name="draft">Πρόχειρο</string>
|
||||
<string name="sending">Γίνεται αποστολή…</string>
|
||||
<string name="export_messages">Εξαγωγή μηνυμάτων</string>
|
||||
<string name="import_messages">Εισαγωγή μηνυμάτων</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Νέα συνομιλία</string>
|
||||
|
|
|
@ -2,12 +2,21 @@
|
|||
<string name="app_name">Mensajería SMS Simple</string>
|
||||
<string name="app_launcher_name">Mensajería SMS</string>
|
||||
<string name="type_a_message">Escribe un mensaje…</string>
|
||||
<string name="message_not_sent">El mensaje no se ha enviado.</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">Añadir persona</string>
|
||||
<string name="attachment">Archivo adjunto</string>
|
||||
<string name="no_conversations_found">No se han encontrado conversaciones</string>
|
||||
<string name="start_conversation">Inicia una conversación</string>
|
||||
<string name="reply">Responder</string>
|
||||
<string name="show_character_counter">Mostrar un contador de caracteres al escribir mensajes</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nueva conversación</string>
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<resources>
|
||||
<string name="app_name">Simple SMS Messenger</string>
|
||||
<string name="app_launcher_name">Viestit</string>
|
||||
<string name="type_a_message">Lähetä viesti…</string>
|
||||
<string name="message_not_sent_short">Viestiä ei lähetetty</string>
|
||||
<string name="message_not_sent_touch_retry">Ei lähetetty. Yritä uudelleen koskemalla.</string>
|
||||
<string name="message_sending_error">Viestiäsi henkilölle \'%s\' ei lähetetty.</string>
|
||||
<string name="add_person">Lisää henkilö</string>
|
||||
<string name="attachment">Liite</string>
|
||||
<string name="no_conversations_found">Tallennettuja keskusteluja ei löytyny</string>
|
||||
<string name="start_conversation">Aloita keskustelu</string>
|
||||
<string name="reply">Vastaa</string>
|
||||
<string name="show_character_counter">Näytä merkkimäärä kirjoittaessasi viestejä</string>
|
||||
<string name="loading_messages">Ladataan viestejä…</string>
|
||||
<string name="no_reply_support">Lähettäjä ei tue vastauksia</string>
|
||||
<string name="draft">Luonnos</string>
|
||||
<string name="sending">Lähetetään…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Uusi keskustelu</string>
|
||||
<string name="add_contact_or_number">Lisää yhteystieto tai numero…</string>
|
||||
<string name="suggestions">Ehdotuksia</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Vastaanotettu tekstiviesti</string>
|
||||
<string name="new_message">Uusi viesti</string>
|
||||
<string name="mark_as_read">Merkitse luetuksi</string>
|
||||
<string name="mark_as_unread">Merkitse lukemattomaksi</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Haluatko varmasti poistaa kaikki tämän keskustelun viestit?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">yhden keskustelun</item>
|
||||
<item quantity="other">%d keskustelua</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">yhden viestin</item>
|
||||
<item quantity="other">%d viestiä</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Miksi sovellus vaatii Internet-yhteyden?</string>
|
||||
<string name="faq_1_text">Valitettavasti sitä tarvitaan multimediaviestin-liitteiden lähettämiseen. Multimediaviestien lähettämättä jättäminen olisi todella valtava haitta muihin sovelluksiin verrattuna, joten päätimme mennä tällä tavalla.
|
||||
Kuten yleensä, mainoksia, seurantaa tai analytiikkaa ei kuitenkaan ole, iternetiä käytetään vain multimediaviestien lähettämiseen.</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
|
||||
<string name="app_title">Simple SMS Messenger - Manage messages easily</string>
|
||||
<!-- Short description has to have max 80 characters -->
|
||||
<string name="app_short_description">An easy and quick way of managing SMS and MMS messages without ads.</string>
|
||||
<string name="app_long_description">
|
||||
A great way to stay in touch with your relatives, by sending both SMS and MMS messages. The app properly handles group messaging too, just like blocking numbers from Android 7+.
|
||||
|
||||
It offers many date formats to choose from, to make you feel comfortable at using it. You can toggle between 12 and 24 hours time format too.
|
||||
|
||||
It has a really tiny app size compared to the competition, making it really fast to download.
|
||||
|
||||
It comes with material design and dark theme by default, provides great user experience for easy usage. The lack of internet access gives you more privacy, security and stability than other apps.
|
||||
|
||||
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
|
||||
|
||||
<b>Check out the full suite of Simple Tools here:</b>
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
<b>Facebook:</b>
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
<b>Reddit:</b>
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -0,0 +1,82 @@
|
|||
<resources>
|
||||
<string name="app_name">Simple SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS Messenger</string>
|
||||
<string name="type_a_message">Écrivez un message…</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">Ajouter une personne</string>
|
||||
<string name="attachment">Pièce jointe</string>
|
||||
<string name="no_conversations_found">Aucune conversation enregistrée n\'a été trouvée</string>
|
||||
<string name="start_conversation">Commencer une conversation</string>
|
||||
<string name="reply">Répondre</string>
|
||||
<string name="show_character_counter">Show a character counter at writing messages</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nouvelle conversation</string>
|
||||
<string name="add_contact_or_number">Ajouter un contact ou un numéro…</string>
|
||||
<string name="suggestions">Suggestions</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">SMS reçu</string>
|
||||
<string name="new_message">Nouveau message</string>
|
||||
<string name="mark_as_read">Marquer comme lu</string>
|
||||
<string name="mark_as_unread">Marquer comme non lu</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Êtes-vous sûr de vouloir supprimer tous les messages de cette conversation ?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d conversation</item>
|
||||
<item quantity="other">%d conversations</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d message</item>
|
||||
<item quantity="other">%d messages</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Pourquoi cette application a besoin d\'un accès à internet ?</string>
|
||||
<string name="faq_1_text">Malheureusement, cela est nécessaire pour envoyer des pièces jointes dans les MMS. Ne pas pouvoir envoyer de MMS serait un énorme désavantage comparé à d\'autres applications, nous avons donc décidé de faire ainsi.
|
||||
Cependant, comme toujours, il n\'y a aucune publicité, traqueur ou analyseur de quelque sorte, internet n\'est utilisé que pour envoyer des MMS.</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
|
||||
<string name="app_title">Simple SMS Messenger - Gérez vos messages aisément</string>
|
||||
<!-- Short description has to have max 80 characters -->
|
||||
<string name="app_short_description">Une façon simple et rapide de gérer ses SMS et MMS sans publicités.</string>
|
||||
<string name="app_long_description">
|
||||
Une excellente façon de rester en contact avec vos proches, en envoyant à la fois des SMS et des MMS. L\'application gère parfaitement les messages de groupe, tout comme le blocage des numéros sur Android 7+.
|
||||
|
||||
Elle offre un choix large de format de date, pour être confortable à utiliser. Vous pouvez choisir entre un format 12 et 24 heures.
|
||||
|
||||
La taille de l\'application est très légère comparée à la concurrence, ce qui la rend rapide à télécharger.
|
||||
|
||||
Avec un Material Design et un thème sombre par défaut, elle offre une excellente expérience utilisateur pour une utilisation facile. L\'absence d\'accès à internet vous donne plus de confidentialité, de sécurité et de stabilité que les autres applications.
|
||||
|
||||
Ne contient aucune publicité ou autorisation inutile. Elle est entièrement open source, avec des couleurs personnalisables.
|
||||
|
||||
<b>Découvrez la suite complète des applications Simple Mobile Tools ici :</b>
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
<b>Facebook:</b>
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
<b>Reddit:</b>
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -0,0 +1,82 @@
|
|||
<resources>
|
||||
<string name="app_name">Simple SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS Messenger</string>
|
||||
<string name="type_a_message">Ketik pesan…</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">Tambahkan Orang</string>
|
||||
<string name="attachment">Lampiran</string>
|
||||
<string name="no_conversations_found">Tidak ada percakapan tersimpan yang ditemukan</string>
|
||||
<string name="start_conversation">Mulailah percakapan</string>
|
||||
<string name="reply">Balas</string>
|
||||
<string name="show_character_counter">Tunjukkan penghitung karakter saat menulis pesan</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Percakapan baru</string>
|
||||
<string name="add_contact_or_number">Tambahkan Kontak atau Nomor…</string>
|
||||
<string name="suggestions">Saran</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Menerima SMS</string>
|
||||
<string name="new_message">Pesan baru</string>
|
||||
<string name="mark_as_read">Tandai sebagai Dibaca</string>
|
||||
<string name="mark_as_unread">Tandai sebagai Belum dibaca</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Apakah Anda yakin ingin menghapus semua pesan dari percakapan ini?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d conversation</item>
|
||||
<item quantity="other">%d conversations</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d pesan</item>
|
||||
<item quantity="other">%d pesan</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Mengapa aplikasi membutuhkan akses ke internet?</string>
|
||||
<string name="faq_1_text">Sayangnya itu diperlukan untuk mengirim lampiran MMS. Tidak dapat mengirim MMS akan menjadi kerugian yang sangat besar dibandingkan dengan aplikasi lain, jadi kami memutuskan untuk menggunakan cara ini.
|
||||
Namun, seperti biasanya, tidak ada iklan, pelacakan atau analitik apa pun, internet hanya digunakan untuk mengirim MMS.</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
|
||||
<string name="app_title">Simple SMS Messenger - Manage messages easily</string>
|
||||
<!-- Short description has to have max 80 characters -->
|
||||
<string name="app_short_description">An easy and quick way of managing SMS and MMS messages without ads.</string>
|
||||
<string name="app_long_description">
|
||||
A great way to stay in touch with your relatives, by sending both SMS and MMS messages. The app properly handles group messaging too, just like blocking numbers from Android 7+.
|
||||
|
||||
It offers many date formats to choose from, to make you feel comfortable at using it. You can toggle between 12 and 24 hours time format too.
|
||||
|
||||
It has a really tiny app size compared to the competition, making it really fast to download.
|
||||
|
||||
It comes with material design and dark theme by default, provides great user experience for easy usage. The lack of internet access gives you more privacy, security and stability than other apps.
|
||||
|
||||
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
|
||||
|
||||
<b>Check out the full suite of Simple Tools here:</b>
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
<b>Facebook:</b>
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
<b>Reddit:</b>
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -0,0 +1,81 @@
|
|||
<resources>
|
||||
<string name="app_name">SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS Messenger</string>
|
||||
<string name="type_a_message">Componi messaggio</string>
|
||||
<string name="message_not_sent_short">Messaggio non inviato</string>
|
||||
<string name="message_not_sent_touch_retry">Non inviato. Tocca per riprovare</string>
|
||||
<string name="message_sending_error">Il tuo messaggio a \'%s\' non è stato inviato</string>
|
||||
<string name="add_person">Aggiungi persona</string>
|
||||
<string name="attachment">Allegato</string>
|
||||
<string name="no_conversations_found">Nessuna conversazione trovata</string>
|
||||
<string name="start_conversation">Inizia conversazione</string>
|
||||
<string name="reply">Rispondi</string>
|
||||
<string name="show_character_counter">Mostra contatore caratteri</string>
|
||||
<string name="loading_messages">Caricamento messaggi…</string>
|
||||
<string name="no_reply_support">Il mittente non accetta risposte</string>
|
||||
<string name="draft">Bozza</string>
|
||||
<string name="sending">Invio…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nuova conversazione</string>
|
||||
<string name="add_contact_or_number">Inserisci contatto o numero…</string>
|
||||
<string name="suggestions">Suggerimenti</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">SMS ricevuto</string>
|
||||
<string name="new_message">Nuovo messaggio</string>
|
||||
<string name="mark_as_read">Letto</string>
|
||||
<string name="mark_as_unread">Non letto</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Vuoi cancellare l\'intera conversazione?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d conversazione</item>
|
||||
<item quantity="other">%d conversazioni</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d messaggio</item>
|
||||
<item quantity="other">%d messaggi</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Perché l\'applicazione richiede l\'accesso ad internet?</string>
|
||||
<string name="faq_1_text">Purtroppo è necessario per poter inviare gli allegati degli MMS. Non essere in grado di inviare gli MMS sarebbe un grosso svantaggio in confronto ad altre applicazioni, quindi abbiamo deciso di intraprendere questa strada.
|
||||
Ad ogni modo, come sempre, non ci sono pubblicità o tracciamenti, internet è utilizzato soltanto per l\'invio degli 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>
|
|
@ -0,0 +1,80 @@
|
|||
<resources>
|
||||
<string name="app_name">Simple ショートメール</string>
|
||||
<string name="app_launcher_name">ショートメール</string>
|
||||
<string name="type_a_message">メッセージを入力してください…</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">宛先を追加</string>
|
||||
<string name="attachment">添付</string>
|
||||
<string name="no_conversations_found">保存された会話はありません</string>
|
||||
<string name="start_conversation">会話を始める</string>
|
||||
<string name="reply">返信</string>
|
||||
<string name="show_character_counter">メッセージ入力中に文字カウントを表示する</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">新しい会話</string>
|
||||
<string name="add_contact_or_number">連絡先や電話番号を追加する…</string>
|
||||
<string name="suggestions">おすすめ</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">受信したショートメール</string>
|
||||
<string name="new_message">新しいメッセージ</string>
|
||||
<string name="mark_as_read">既読にする</string>
|
||||
<string name="mark_as_unread">未読にする</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">本当にこの会話の全てのメッセージを削除しますか?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d件の会話</item>
|
||||
<item quantity="other">%d件の会話</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%件のメッセージ</item>
|
||||
<item quantity="other">%件のメッセージ</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">なぜアプリ使用にインターネットへのアクセスが必要なのですか?</string>
|
||||
<string name="faq_1_text">生憎、MMS(マルチメディアメッセージサービス)にインターネットが必要となります。他のアプリと比較して、MMSを使用出来ないと大きな損になるので、こうすることに決めました。
|
||||
ただし、通常通り広告・追跡・分析は一切行われず、MMS送信にのみインターネットが使われます。</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
|
||||
<string name="app_title">シンプルなSMSメッセンジャー-メッセージを簡単に管理</string>
|
||||
<!-- Short description has to have max 80 characters -->
|
||||
<string name="app_short_description">SMS、MMSメッセージをすばやく送信。広告なしのきれいで美しいカスタマイズ可能なインターフェイス</string>
|
||||
<string name="app_long_description">
|
||||
SMSやMMSメッセージは親戚と連絡を取るのに便利です。グループメッセージも可能で、アンドロイド7以降のように連絡先をブロックすることも出来ます。
|
||||
|
||||
日付フォーマットも複数から使いやすいものを選ぶことができます。日時設定は12時間設定と24時間設定に切り変えることも可能です。
|
||||
|
||||
容量が他のアプリと比べて小さいため早くダウンロードができます。
|
||||
|
||||
広告や不要な権限はありません。 完全にオープンソースで、カラーもカスタマイズ可能。
|
||||
|
||||
<b>シンプルツールの完全なリストはこちらからご確認ください:</b>
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
<b> Facebook:</b>
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
<b> Reddit:</b>
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -2,12 +2,21 @@
|
|||
<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="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</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>
|
||||
<string name="reply">Reply</string>
|
||||
<string name="show_character_counter">Show a character counter at writing messages</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Naujas pokalbis</string>
|
||||
|
|
|
@ -2,12 +2,21 @@
|
|||
<string name="app_name">Simple SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS മെസഞ്ചർ</string>
|
||||
<string name="type_a_message">മെസ്സേജ് ടൈപ്പ് ചെയ്യുക</string>
|
||||
<string name="message_not_sent">മെസ്സേജ് അയച്ചിട്ടില്ല.</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">വ്യക്തിയെ ചേർക്കുക</string>
|
||||
<string name="attachment">അറ്റാച്ചുമെന്റ്</string>
|
||||
<string name="no_conversations_found">സ്റ്റോർ ചെയ്ത സംഭാഷണങ്ങളൊന്നും കണ്ടെത്തിയില്ല</string>
|
||||
<string name="start_conversation">ഒരു സംഭാഷണം ആരംഭിക്കുക</string>
|
||||
<string name="reply">ഒരു സംഭാഷണം ആരംഭിക്കുക</string>
|
||||
<string name="show_character_counter">Show a character counter at writing messages</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">പുതിയ സംഭാഷണം</string>
|
||||
|
|
|
@ -2,12 +2,21 @@
|
|||
<string name="app_name">Eenvoudig Berichtenbeheer (SMS)</string>
|
||||
<string name="app_launcher_name">Berichten</string>
|
||||
<string name="type_a_message">Typ een bericht…</string>
|
||||
<string name="message_not_sent">Bericht niet verzonden.</string>
|
||||
<string name="message_not_sent_short">Bericht niet verzonden</string>
|
||||
<string name="message_not_sent_touch_retry">Niet verzonden. Probeer het nogmaals.</string>
|
||||
<string name="message_sending_error">Bericht naar \'%s\' is niet verzonden</string>
|
||||
<string name="add_person">Persoon toevoegen</string>
|
||||
<string name="attachment">Bijlage</string>
|
||||
<string name="no_conversations_found">Geen opgeslagen berichten gevonden</string>
|
||||
<string name="start_conversation">Een gesprek starten</string>
|
||||
<string name="reply">Beantwoorden</string>
|
||||
<string name="show_character_counter">Teller voor het aantal tekens weergeven</string>
|
||||
<string name="loading_messages">Berichten laden…</string>
|
||||
<string name="no_reply_support">Afzender ondersteunt geen antwoorden</string>
|
||||
<string name="draft">Concept</string>
|
||||
<string name="sending">Versturen…</string>
|
||||
<string name="export_messages">Berichten exporteren</string>
|
||||
<string name="import_messages">Berichten importeren</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nieuw gesprek</string>
|
||||
|
|
|
@ -2,12 +2,21 @@
|
|||
<string name="app_name">Simple SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS Messenger</string>
|
||||
<string name="type_a_message">Escrever uma mensagem…</string>
|
||||
<string name="message_not_sent">Mensagem não enviada</string>
|
||||
<string name="message_not_sent_short">Mensagem não enviada</string>
|
||||
<string name="message_not_sent_touch_retry">Não enviada. Toque para tentar novamente.</string>
|
||||
<string name="message_sending_error">A mensagem enviada para \'%s\' não foi enviada.</string>
|
||||
<string name="add_person">Adicionar pessoa</string>
|
||||
<string name="attachment">Anexo</string>
|
||||
<string name="no_conversations_found">Não foram encontradas conversas</string>
|
||||
<string name="start_conversation">Iniciar uma conversa</string>
|
||||
<string name="reply">Responder</string>
|
||||
<string name="show_character_counter">Mostrar número de caracteres ao escrever a mensagem</string>
|
||||
<string name="loading_messages">A carregar mensagens…</string>
|
||||
<string name="no_reply_support">O remetente não aceita respostas</string>
|
||||
<string name="draft">Rascunho</string>
|
||||
<string name="sending">A enviar…</string>
|
||||
<string name="export_messages">Exportar mensagens</string>
|
||||
<string name="import_messages">Importar mensagens</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nova conversa</string>
|
||||
|
|
|
@ -2,12 +2,21 @@
|
|||
<string name="app_name">Simple SMS Messenger</string>
|
||||
<string name="app_launcher_name">Сообщения</string>
|
||||
<string name="type_a_message">Введите сообщение…</string>
|
||||
<string name="message_not_sent">Сообщение не отправлено.</string>
|
||||
<string name="message_not_sent_short">Сообщение не отправлено</string>
|
||||
<string name="message_not_sent_touch_retry">Не отправлено. Нажмите, чтобы повторить попытку.</string>
|
||||
<string name="message_sending_error">Ваше сообщение для \"%s\" не было отправлено</string>
|
||||
<string name="add_person">Добавить участника</string>
|
||||
<string name="attachment">Вложение</string>
|
||||
<string name="no_conversations_found">Нет сохранённых переписок</string>
|
||||
<string name="start_conversation">Начать переписку</string>
|
||||
<string name="reply">Ответ</string>
|
||||
<string name="show_character_counter">Показывать счётчик символов при написании сообщений</string>
|
||||
<string name="loading_messages">Загрузка сообщений…</string>
|
||||
<string name="no_reply_support">Отправитель не поддерживает ответы</string>
|
||||
<string name="draft">Черновик</string>
|
||||
<string name="sending">Отправка…</string>
|
||||
<string name="export_messages">Экспорт сообщений</string>
|
||||
<string name="import_messages">Импорт сообщений</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Новая переписка</string>
|
||||
|
@ -15,13 +24,13 @@
|
|||
<string name="suggestions">Предложения</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Получено SMS</string>
|
||||
<string name="channel_received_sms">Получено сообщение</string>
|
||||
<string name="new_message">Новое сообщение</string>
|
||||
<string name="mark_as_read">Прочитано</string>
|
||||
<string name="mark_as_unread">Не прочитано</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Вы уверены, что хотите удалить все сообщения в этой переписке?</string>
|
||||
<string name="delete_whole_conversation_confirmation">Удалить все сообщения в этой переписке?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
|
|
|
@ -2,12 +2,21 @@
|
|||
<string name="app_name">Jednoduché SMS správy</string>
|
||||
<string name="app_launcher_name">SMS správy</string>
|
||||
<string name="type_a_message">Zadajte správu…</string>
|
||||
<string name="message_not_sent">Správa nebola odoslaná.</string>
|
||||
<string name="message_not_sent_short">Správa nebola odoslaná</string>
|
||||
<string name="message_not_sent_touch_retry">Správa neodoslaná. Dotykom to skúste znova.</string>
|
||||
<string name="message_sending_error">Vaša správa pre \'%s\' nebola odoslaná</string>
|
||||
<string name="add_person">Pridať osobu</string>
|
||||
<string name="attachment">Príloha</string>
|
||||
<string name="no_conversations_found">Nenašli sa žiadne uložené konverzácie</string>
|
||||
<string name="start_conversation">Začať konverzáciu</string>
|
||||
<string name="reply">Odpovedať</string>
|
||||
<string name="show_character_counter">Zobraziť počítadlo znakov pri písaní správ</string>
|
||||
<string name="loading_messages">Načítanie správ…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Koncept</string>
|
||||
<string name="sending">Odosiela sa…</string>
|
||||
<string name="export_messages">Exportovať správy</string>
|
||||
<string name="import_messages">Importovať správy</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nová konverzácia</string>
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<resources>
|
||||
<string name="app_name">Basit SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS Messenger</string>
|
||||
<string name="type_a_message">Bir mesaj yazın…</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">Kişi Ekle</string>
|
||||
<string name="attachment">Ek</string>
|
||||
<string name="no_conversations_found">Kaydedilmiş görüşme bulunamadı</string>
|
||||
<string name="start_conversation">Bir görüşme başlat</string>
|
||||
<string name="reply">Yanıtla</string>
|
||||
<string name="show_character_counter">Mesaj yazarken bir karakter sayacı göster</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Yeni görüşme</string>
|
||||
<string name="add_contact_or_number">Kişi veya Numara Ekle…</string>
|
||||
<string name="suggestions">Öneriler</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">SMS alındı</string>
|
||||
<string name="new_message">Yeni mesaj</string>
|
||||
<string name="mark_as_read">Okundu olarak işaretle</string>
|
||||
<string name="mark_as_unread">Okunmadı olarak işaretle</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Bu görüşmenin tüm mesajlarını silmek istediğinizden emin misiniz?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d görüşme</item>
|
||||
<item quantity="other">%d görüşme</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d mesaj</item>
|
||||
<item quantity="other">%d mesaj</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Uygulama neden internete erişim gerektiriyor?</string>
|
||||
<string name="faq_1_text">Ne yazık ki MMS eklerini göndermek için gerekli. MMS gönderememek, diğer uygulamalara kıyasla gerçekten çok büyük bir dezavantaj olacaktır, bu yüzden bu şekilde gitmeye karar verdik.
|
||||
Bununla birlikte, genellikle olduğu gibi, hiçbir reklam, izleme veya analiz yoktur, internet yalnızca MMS göndermek için kullanılır.</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">Basit SMS Messenger - Mesajları kolayca yönetin</string>
|
||||
<!-- Short description has to have max 80 characters -->
|
||||
<string name="app_short_description">SMS ve MMS mesajlarını reklamsız yönetmenin kolay ve hızlı bir yolu.</string>
|
||||
<string name="app_long_description">
|
||||
Hem SMS hem de MMS göndererek iletişim halinde kalmanın harika bir yolu. Uygulama, tıpkı Android 7 ve üzerinden gelen numaraları engellemek gibi grup mesajlaşmasını da düzgün bir şekilde yönetiyor.
|
||||
|
||||
Kullanırken kendinizi rahat hissetmeniz için pek çok tarih formatı sunar. 12 ve 24 saatlik zaman formatı arasında da geçiş yapabilirsiniz.
|
||||
|
||||
Rakiplerine kıyasla gerçekten çok küçük bir uygulama boyutuna sahip ve indirmeyi gerçekten hızlı hale getiriyor.
|
||||
|
||||
Varsayılan olarak materyal tasarım ve koyu tema ile birlikte gelir, kolay kullanım için harika bir kullanıcı deneyimi sağlar. İnternet erişiminin olmaması size diğer uygulamalardan daha fazla gizlilik, güvenlik ve istikrar sağlar.
|
||||
|
||||
Hiçbir reklam veya gereksiz izinler içermez. Tamamen açık kaynaklıdır, özelleştirilebilir renkler sağlar.
|
||||
|
||||
<b>Basit Araçlar paketinin tamamına buradan göz atın:</b>
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
<b>Facebook:</b>
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
<b>Reddit:</b>
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -0,0 +1,82 @@
|
|||
<resources>
|
||||
<string name="app_name">Simple SMS Messenger</string>
|
||||
<string name="app_launcher_name">Повідомлення</string>
|
||||
<string name="type_a_message">Введіть повідомлення…</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">Додати учасник а</string>
|
||||
<string name="attachment">Вкладення</string>
|
||||
<string name="no_conversations_found">Немає збережених листувань</string>
|
||||
<string name="start_conversation">Почати листування</string>
|
||||
<string name="reply">Відповідь</string>
|
||||
<string name="show_character_counter">Показувати кількість символів</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Нове листування</string>
|
||||
<string name="add_contact_or_number">Додати контакт аба номер…</string>
|
||||
<string name="suggestions">Пропозиція</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Отримано повідомлення</string>
|
||||
<string name="new_message">Нове повідомлення</string>
|
||||
<string name="mark_as_read">Прочитано</string>
|
||||
<string name="mark_as_unread">Не прочитано</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Видалити усі повідомлення у цьому листуванні?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d листування</item>
|
||||
<item quantity="many">%d листувань</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d повідомлення</item>
|
||||
<item quantity="many">%d повідомлень</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Чому додаток потрубує доступу до інтернету?</string>
|
||||
<string name="faq_1_text">Нажаль, це необхідно для відправки вкладень MMS. Неспроможність надсилати MMS-повідомлення була б великим недоліком нашого додатку порівняно з іншими, тому ми так зробили.
|
||||
Тим не менше, як і в інших наших додатках, цей не містить реклами, відстеження та аналітики. Інтернет використовується лише для відправки MMS.</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
|
||||
<string name="app_title">Simple SMS Messenger - просте управління SMS</string>
|
||||
<!-- Short description has to have max 80 characters -->
|
||||
<string name="app_short_description">Простий та швидкий спосіб управління повідомленнями SMS та MMS без реклами.</string>
|
||||
<string name="app_long_description">
|
||||
SMS/MMS-повідомлення — це чудовий спосіб підтримувати зв\'язок із близькими. Додаток також правильно опрацьовує групові повідомлення та блокування номера на Android 7+.
|
||||
|
||||
Підтримується багато форматів часу, щоб вам було зручно користуватися додатком. Також ви можете вибирати між 12- та 24-годинним форматом часу.
|
||||
|
||||
Наш додаток має малий розмір в порівнянні з додатками конкурентів, що робить його завантаження таким швидким.
|
||||
|
||||
За замовчуванням використовується матеріальний дизайн та темна тема, що забезпечую зручне використання. Відсутність доступу в інтернет забезпечую вам більшу приватність порівняно з іншими додатками.
|
||||
|
||||
Цей додаток не буде показувати рекламу, потрібні лише найнеобхідніші дозволи. Додаток має повністю відкритий програмний код, кольори оформлення можна легко налаштувати.
|
||||
|
||||
<b>Ознакомьтеся з повним набором інструментів Simple тут:</b>
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
<b>Facebook:</b>
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
<b>Reddit:</b>
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -0,0 +1,82 @@
|
|||
<resources>
|
||||
<string name="app_name">简易短信</string>
|
||||
<string name="app_launcher_name">短信</string>
|
||||
<string name="type_a_message">输入一个消息…</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">添加人</string>
|
||||
<string name="attachment">附件</string>
|
||||
<string name="no_conversations_found">未找到保存的对话</string>
|
||||
<string name="start_conversation">开始一个对话</string>
|
||||
<string name="reply">回复</string>
|
||||
<string name="show_character_counter">Show a character counter at writing messages</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">新的对话</string>
|
||||
<string name="add_contact_or_number">添加联系人或者号码…</string>
|
||||
<string name="suggestions">建议</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">接收到的短信</string>
|
||||
<string name="new_message">新消息</string>
|
||||
<string name="mark_as_read">标记为已读</string>
|
||||
<string name="mark_as_unread">标记为未读</string>
|
||||
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">您确定您想要删除这个对话的所有消息?</string>
|
||||
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d 个对话</item>
|
||||
<item quantity="other">%d 个对话</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d 个消息</item>
|
||||
<item quantity="other">%d 个消息</item>
|
||||
</plurals>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">为什么该应用需要访问互联网?</string>
|
||||
<string name="faq_1_text">很遗憾这对于发送彩信附件是必须的。如果不能发送彩信的话这相比其他应用会是一个巨大的劣势,所以我们决定这么采取现在的方式。
|
||||
但是和其他应用一样,不包含广告、追踪或者任意的分析工具, 互联网访问仅用于发送彩信。</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
|
||||
<string name="app_title">简易短信 - 轻松管理消息</string>
|
||||
<!-- Short description has to have max 80 characters -->
|
||||
<string name="app_short_description">无广告的管理短信和彩信的轻松和简便方式。</string>
|
||||
<string name="app_long_description">
|
||||
发送短信和彩信是一个和您亲友保持联系的绝佳方式。这个应用也可以可以正确处理群消息,在 Android 7 以上版本亦可阻止号码。
|
||||
|
||||
它提供多个日期格式以便选择,符合您使用习惯。您也可以在 12 和 24 时制格式之间选择。
|
||||
|
||||
它相对于其他竞品有着极小的大小,使得其下载非常快。
|
||||
|
||||
它遵循质感设计且默认应用黑暗模式,提供便于使用的极佳的用户体验。
|
||||
|
||||
不包含广告及非必要的权限,而且完全开放源代码,并提供自定义颜色。
|
||||
|
||||
<b>于此查看简易工具系列全套:</b>
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
<b>Facebook:</b>
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
<b>Reddit:</b>
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -0,0 +1,5 @@
|
|||
<resources>
|
||||
|
||||
<string name="package_name">com.simplemobiletools.smsmessenger</string>
|
||||
|
||||
</resources>
|
|
@ -2,12 +2,21 @@
|
|||
<string name="app_name">Simple SMS Messenger</string>
|
||||
<string name="app_launcher_name">SMS Messenger</string>
|
||||
<string name="type_a_message">Type a message…</string>
|
||||
<string name="message_not_sent">Message has not been sent.</string>
|
||||
<string name="message_not_sent_short">Message not sent</string>
|
||||
<string name="message_not_sent_touch_retry">Not sent. Touch to retry.</string>
|
||||
<string name="message_sending_error">Your message to \'%s\' has not been sent</string>
|
||||
<string name="add_person">Add Person</string>
|
||||
<string name="attachment">Attachment</string>
|
||||
<string name="no_conversations_found">No stored conversations have been found</string>
|
||||
<string name="start_conversation">Start a conversation</string>
|
||||
<string name="reply">Reply</string>
|
||||
<string name="show_character_counter">Show a character counter at writing messages</string>
|
||||
<string name="loading_messages">Loading messages…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">New conversation</string>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<searchable
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:hint="@string/search"
|
||||
android:label="@string/app_name"/>
|
|
@ -1,7 +1,7 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.4.10'
|
||||
ext.kotlin_version = '1.4.30'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
* Fixed some smaller glitches + translation improvements
|
|
@ -0,0 +1,4 @@
|
|||
* Allow dialing or copying selected conversation phone numbers
|
||||
* Allow copying specific parts of messages into clipboard
|
||||
* Adding an option to show character counter at outgoing messages
|
||||
* Couple other UI, translation and stability improvements
|
|
@ -0,0 +1,3 @@
|
|||
* Properly show private contact names at group conversations
|
||||
* Fixed private contacts not being visible on Android 11+
|
||||
* Some stability and translation improvements
|
|
@ -0,0 +1,3 @@
|
|||
* Cache messages for better performance
|
||||
* Add a scrollbar on the main screen
|
||||
* Adding some stability and translation improvements
|
|
@ -0,0 +1 @@
|
|||
* Fixing a crash at devices with multiple SIM cards
|
|
@ -0,0 +1 @@
|
|||
Fixed messages not being sent in some cases
|
|
@ -0,0 +1,3 @@
|
|||
* Prefetch all messages at first launch for better performance
|
||||
* Require Simple Thank You for color customization
|
||||
* Some stability, translation and UX improvements
|
|
@ -0,0 +1,4 @@
|
|||
* Improved delivery reports, show a notification at failed messages
|
||||
* Allow resending failed messages with tapping them
|
||||
* Added multiple dual-SIM related improvements
|
||||
* Many other UX, stability, performance and translation improvements
|
|
@ -0,0 +1,3 @@
|
|||
* Fixed a glitch with "Sending..." stuck at messages
|
||||
* Allow selecting a phone numbers at contacts with multiple ones
|
||||
* Some translation and stability improvements
|
|
@ -0,0 +1,3 @@
|
|||
* Fixed a glitch with inability to send messages in empty conversations
|
||||
* Adding a settings item for quickly getting into notification settings
|
||||
* Some stability and translation improvements
|
|
@ -0,0 +1 @@
|
|||
* Adding some stability and translation improvements
|
|
@ -0,0 +1,3 @@
|
|||
* Added Search
|
||||
* Added a White theme with special handling
|
||||
* Some stability and translation improvements
|
Before Width: | Height: | Size: 151 KiB |
Before Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 224 KiB |
After Width: | Height: | Size: 352 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 172 KiB |
|
@ -1,6 +1,6 @@
|
|||
#Sat Mar 06 06:37:14 PST 2021
|
||||
#Sat Mar 06 06:37:14 PST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||
|
|