mirror of
https://github.com/SimpleMobileTools/Simple-SMS-Messenger.git
synced 2025-02-18 12:40:46 +01:00
commit
cdb135dfbf
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
github: [tibbi]
|
||||
patreon: tiborkaputa
|
||||
custom: ["https://www.paypal.com/paypalme/simplemobiletools", "https://www.simplemobiletools.com/donate"]
|
31
CHANGELOG.md
31
CHANGELOG.md
@ -1,6 +1,37 @@
|
||||
Changelog
|
||||
==========
|
||||
|
||||
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)*
|
||||
----------------------------
|
||||
|
||||
|
@ -16,8 +16,8 @@ android {
|
||||
applicationId "com.simplemobiletools.smsmessenger"
|
||||
minSdkVersion 22
|
||||
targetSdkVersion 30
|
||||
versionCode 18
|
||||
versionName "5.5.0"
|
||||
versionCode 23
|
||||
versionName "5.7.0"
|
||||
setProperty("archivesBaseName", "sms-messenger")
|
||||
}
|
||||
|
||||
@ -56,13 +56,14 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.simplemobiletools:commons:5.31.23'
|
||||
implementation 'com.simplemobiletools:commons:5.32.19'
|
||||
implementation 'org.greenrobot:eventbus:3.2.0'
|
||||
implementation 'com.klinkerapps:android-smsmms:5.2.6'
|
||||
implementation 'com.github.tibbi:IndicatorFastScroll:08f512858a'
|
||||
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"
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
@ -183,46 +182,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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,17 +228,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)
|
||||
|
@ -8,10 +8,7 @@ import android.view.WindowManager
|
||||
import com.google.gson.Gson
|
||||
import com.reddit.indicatorfastscroll.FastScrollItemIndicator
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.MyContactsContentProvider
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
|
||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import com.simplemobiletools.commons.models.SimpleContact
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.adapters.ContactsAdapter
|
||||
@ -144,7 +141,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)
|
||||
@ -194,7 +191,7 @@ class NewConversationActivity : SimpleActivity() {
|
||||
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(numbers).toInt())
|
||||
putExtra(THREAD_ID, getThreadId(numbers))
|
||||
putExtra(THREAD_TITLE, name)
|
||||
putExtra(THREAD_TEXT, text)
|
||||
putExtra(THREAD_NUMBER, number)
|
||||
|
@ -53,15 +53,16 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ 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 threadItems = ArrayList<ThreadItem>()
|
||||
@ -72,7 +72,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
|
||||
}
|
||||
@ -81,7 +81,10 @@ class ThreadActivity : SimpleActivity() {
|
||||
bus!!.register(this)
|
||||
handlePermission(PERMISSION_READ_PHONE_STATE) {
|
||||
if (it) {
|
||||
setupThread()
|
||||
setupButtons()
|
||||
setupCachedMessages {
|
||||
setupThread()
|
||||
}
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
@ -99,15 +102,16 @@ class ThreadActivity : SimpleActivity() {
|
||||
}
|
||||
|
||||
private fun setupThread() {
|
||||
val privateCursor = getMyContactsCursor().loadInBackground()
|
||||
val privateCursor = getMyContactsCursor()?.loadInBackground()
|
||||
ensureBackgroundThread {
|
||||
val cachedMessagesCode = messages.hashCode()
|
||||
messages = getMessages(threadId)
|
||||
participants = if (messages.isEmpty()) {
|
||||
getThreadParticipants(threadId, null)
|
||||
} else {
|
||||
messages.first().participants
|
||||
if (messages.hashCode() == cachedMessagesCode) {
|
||||
return@ensureBackgroundThread
|
||||
}
|
||||
|
||||
setupParticipants()
|
||||
|
||||
// check if no participant came from a privately stored contact in Simple Contacts
|
||||
privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
if (privateContacts.isNotEmpty()) {
|
||||
@ -140,50 +144,17 @@ class ThreadActivity : SimpleActivity() {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
messages.chunked(30).forEach { currentMessages ->
|
||||
messagesDB.insertMessages(*currentMessages.toTypedArray())
|
||||
}
|
||||
|
||||
setupAttachmentSizes()
|
||||
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()
|
||||
}
|
||||
|
||||
setupThreadTitle()
|
||||
setupSIMSelector()
|
||||
}
|
||||
}
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@ -224,6 +195,25 @@ 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 setupAdapter() {
|
||||
threadItems = getThreadItems()
|
||||
invalidateOptionsMenu()
|
||||
@ -290,7 +280,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)
|
||||
@ -315,6 +305,53 @@ 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
|
||||
@ -335,6 +372,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)
|
||||
@ -430,7 +471,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
|
||||
private fun markAsUnread() {
|
||||
ensureBackgroundThread {
|
||||
conversationsDB.markUnread(threadId.toLong())
|
||||
conversationsDB.markUnread(threadId)
|
||||
markThreadMessagesUnread(threadId)
|
||||
runOnUiThread {
|
||||
finish()
|
||||
@ -474,7 +515,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
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) {
|
||||
@ -568,6 +609,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
|
||||
val settings = Settings()
|
||||
settings.useSystemSending = true
|
||||
settings.deliveryReports = true
|
||||
|
||||
val SIMId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId
|
||||
if (SIMId != null) {
|
||||
@ -588,12 +630,14 @@ 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())
|
||||
transaction.sendNewMessage(message, threadId)
|
||||
|
||||
thread_type_message.setText("")
|
||||
attachmentUris.clear()
|
||||
@ -601,6 +645,8 @@ class ThreadActivity : SimpleActivity() {
|
||||
thread_attachments_wrapper.removeAllViews()
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
} catch (e: Error) {
|
||||
toast(e.localizedMessage ?: getString(R.string.unknown_error_occurred))
|
||||
}
|
||||
}
|
||||
|
||||
@ -661,10 +707,16 @@ class ThreadActivity : SimpleActivity() {
|
||||
@Subscribe(threadMode = ThreadMode.ASYNC)
|
||||
fun refreshMessages(event: Events.RefreshMessages) {
|
||||
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 {
|
||||
messagesDB.insertOrIgnore(it)
|
||||
}
|
||||
|
||||
setupAdapter()
|
||||
}
|
||||
}
|
||||
|
@ -63,9 +63,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() {}
|
||||
|
||||
@ -153,11 +153,11 @@ 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 {
|
||||
@ -193,7 +193,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)
|
||||
@ -218,7 +218,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
|
||||
|
@ -81,9 +81,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() {}
|
||||
|
||||
@ -145,7 +145,14 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
||||
|
||||
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)
|
||||
@ -162,7 +169,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)
|
||||
@ -179,7 +186,7 @@ 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
|
||||
|
||||
@ -192,7 +199,7 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList<ThreadItem
|
||||
|
||||
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)
|
||||
@ -218,7 +225,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)
|
||||
|
@ -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`)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -27,7 +27,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 +44,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,15 +85,15 @@ 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), ArrayList(), ArrayList())
|
||||
val isMMS = false
|
||||
@ -100,7 +109,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 +134,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 +152,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 +174,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 +192,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 +215,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 +233,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 +248,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 +266,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 +293,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 +320,7 @@ fun Context.getThreadSnippet(threadId: Int): String {
|
||||
return snippet
|
||||
}
|
||||
|
||||
fun Context.getThreadParticipants(threadId: Int, contactsMap: HashMap<Int, SimpleContact>?): ArrayList<SimpleContact> {
|
||||
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,8 +342,8 @@ 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 name = namePhoto.name
|
||||
val photoUri = namePhoto.photoUri ?: ""
|
||||
val contact = SimpleContact(addressId, addressId, name, photoUri, arrayListOf(phoneNumber), ArrayList(), ArrayList())
|
||||
participants.add(contact)
|
||||
}
|
||||
@ -356,10 +363,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 +417,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()) {
|
||||
@ -427,7 +444,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 +471,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,10 +485,10 @@ 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())
|
||||
@ -484,17 +501,23 @@ fun Context.deleteConversation(threadId: Int) {
|
||||
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)
|
||||
@ -503,9 +526,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)
|
||||
@ -515,9 +539,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)
|
||||
@ -564,23 +589,23 @@ 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 ?: ""
|
||||
var sender = getNameAndPhotoFromPhoneNumber(address).name
|
||||
if (address == sender) {
|
||||
val privateContacts = MyContactsContentProvider.getSimpleContacts(this, privateCursor)
|
||||
sender = privateContacts.firstOrNull { it.doesContainPhoneNumber(address) }?.name ?: address
|
||||
}
|
||||
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
showMessageNotification(address, body, threadID, bitmap, sender)
|
||||
showMessageNotification(address, body, threadId, bitmap, 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()) {
|
||||
@ -602,14 +627,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)
|
||||
@ -622,11 +647,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()
|
||||
@ -652,5 +677,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())
|
||||
}
|
||||
|
@ -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>
|
||||
}
|
@ -23,9 +23,6 @@ interface ConversationsDao {
|
||||
@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,37 @@
|
||||
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("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("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") val 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>)
|
||||
|
@ -1,3 +1,3 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
data class ThreadError(val messageID: Int) : ThreadItem()
|
||||
data class ThreadError(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,25 +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)
|
||||
}
|
||||
|
||||
if (!context.isNumberBlocked(address)) {
|
||||
ensureBackgroundThread {
|
||||
context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
|
||||
|
||||
val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread
|
||||
context.conversationsDB.insertOrUpdate(conversation)
|
||||
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
|
||||
ensureBackgroundThread {
|
||||
messages.forEach {
|
||||
address = it.originatingAddress ?: ""
|
||||
subject = it.pseudoSubject
|
||||
body += it.messageBody
|
||||
date = Math.min(it.timestampMillis, System.currentTimeMillis())
|
||||
threadId = context.getThreadId(address)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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,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"
|
||||
@ -20,9 +25,4 @@
|
||||
android:id="@+id/cab_copy_number"
|
||||
android:title="@string/copy_number_to_clipboard"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/cab_delete"
|
||||
android:icon="@drawable/ic_delete_vector"
|
||||
android:title="@string/delete"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<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="type_a_message">Schreibe eine Nachricht…</string>
|
||||
<string name="message_not_sent">Nachricht wurde nicht gesendet.</string>
|
||||
<string name="add_person">Person hinzufügen</string>
|
||||
<string name="attachment">Anhang</string>
|
||||
@ -9,6 +9,10 @@
|
||||
<string name="start_conversation">Neuen Chat beginnen</string>
|
||||
<string name="reply">Antworten</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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Neuer Chat</string>
|
||||
|
@ -8,7 +8,11 @@
|
||||
<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="show_character_counter">Εμφάνιση μετρητή χαρακτήρων κατά την πληκτρολόγηση μηνυμάτων</string>
|
||||
<string name="loading_messages">Φόρτωση μηνυμάτων…</string>
|
||||
<string name="no_reply_support">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Νέα συνομιλία</string>
|
||||
|
@ -8,7 +8,11 @@
|
||||
<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">Show a character counter at writing messages</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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nueva conversación</string>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<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="type_a_message">Écrivez un message…</string>
|
||||
<string name="message_not_sent">Le message n\'a pas été envoyé.</string>
|
||||
<string name="add_person">Ajouter une personne</string>
|
||||
<string name="attachment">Pièce jointe</string>
|
||||
@ -9,10 +9,14 @@
|
||||
<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>
|
||||
|
||||
<!-- 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="add_contact_or_number">Ajouter un contact ou un numéro…</string>
|
||||
<string name="suggestions">Suggestions</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
|
78
app/src/main/res/values-id/strings.xml
Normal file
78
app/src/main/res/values-id/strings.xml
Normal file
@ -0,0 +1,78 @@
|
||||
<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">Pesan belum terkirim.</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>
|
||||
|
||||
<!-- 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>
|
76
app/src/main/res/values-ja/strings.xml
Normal file
76
app/src/main/res/values-ja/strings.xml
Normal file
@ -0,0 +1,76 @@
|
||||
<resources>
|
||||
<string name="app_name">Simple ショートメール</string>
|
||||
<string name="app_launcher_name">ショートメール</string>
|
||||
<string name="type_a_message">メッセージを入力してください…</string>
|
||||
<string name="message_not_sent">送信されませんでした。</string>
|
||||
<string name="add_person">宛先を追加</string>
|
||||
<string name="attachment">添付</string>
|
||||
<string name="no_conversations_found">保存された会話はありません</string>
|
||||
<string name="start_conversation">会話を始める</string>
|
||||
<string name="reply">返信</string>
|
||||
<string name="show_character_counter">メッセージ入力中に文字カウントを表示する</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>
|
||||
|
||||
<!-- 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>
|
@ -9,6 +9,10 @@
|
||||
<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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Naujas pokalbis</string>
|
||||
|
@ -9,6 +9,10 @@
|
||||
<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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">പുതിയ സംഭാഷണം</string>
|
||||
|
@ -8,7 +8,11 @@
|
||||
<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">Show a character counter at writing messages</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">Sender doesn\'t support replies</string>
|
||||
<string name="draft">Draft</string>
|
||||
<string name="sending">Sending…</string>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nieuw gesprek</string>
|
||||
|
@ -8,7 +8,11 @@
|
||||
<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">Show a character counter at writing messages</string>
|
||||
<string name="show_character_counter">Mostrar número de caracteres ao escrever a mensagem</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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nova conversa</string>
|
||||
|
@ -9,6 +9,10 @@
|
||||
<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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Новая переписка</string>
|
||||
|
@ -9,6 +9,10 @@
|
||||
<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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Nová konverzácia</string>
|
||||
|
78
app/src/main/res/values-tr/strings.xml
Normal file
78
app/src/main/res/values-tr/strings.xml
Normal file
@ -0,0 +1,78 @@
|
||||
<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">Mesaj gönderilmedi.</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>
|
||||
|
||||
<!-- 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>
|
78
app/src/main/res/values-uk/strings.xml
Normal file
78
app/src/main/res/values-uk/strings.xml
Normal file
@ -0,0 +1,78 @@
|
||||
<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">Повідомлення не надіслано. </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>
|
||||
|
||||
<!-- 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>
|
@ -9,6 +9,10 @@
|
||||
<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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">新的对话</string>
|
||||
|
@ -9,6 +9,10 @@
|
||||
<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>
|
||||
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">New conversation</string>
|
||||
|
@ -1,14 +1,14 @@
|
||||
// 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.21'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
3
fastlane/metadata/android/en-US/changelogs/19.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/19.txt
Normal file
@ -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
|
3
fastlane/metadata/android/en-US/changelogs/20.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/20.txt
Normal file
@ -0,0 +1,3 @@
|
||||
* Cache messages for better performance
|
||||
* Add a scrollbar on the main screen
|
||||
* Adding some stability and translation improvements
|
1
fastlane/metadata/android/en-US/changelogs/21.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/21.txt
Normal file
@ -0,0 +1 @@
|
||||
* Fixing a crash at devices with multiple SIM cards
|
1
fastlane/metadata/android/en-US/changelogs/22.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/22.txt
Normal file
@ -0,0 +1 @@
|
||||
Fixed messages not being sent in some cases
|
3
fastlane/metadata/android/en-US/changelogs/23.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/23.txt
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user