Improve SMS delivery report handling
On some phones, SMS message is marked delivered even though it wasn't really delivered, this should fix such issues. Based on AOSP and Signal app
This commit is contained in:
parent
f24001ea3d
commit
08ee7ac700
|
@ -3,9 +3,11 @@ package com.simplemobiletools.smsmessenger.messaging
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.Telephony.Sms
|
import android.provider.Telephony.Sms
|
||||||
import android.telephony.SmsManager
|
import android.telephony.SmsManager
|
||||||
|
import android.telephony.SmsMessage
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.klinker.android.send_message.Message
|
import com.klinker.android.send_message.Message
|
||||||
import com.klinker.android.send_message.Settings
|
import com.klinker.android.send_message.Settings
|
||||||
|
@ -88,6 +90,12 @@ class MessagingUtils(val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSmsMessageFromDeliveryReport(intent: Intent): SmsMessage? {
|
||||||
|
val pdu = intent.getByteArrayExtra("pdu")
|
||||||
|
val format = intent.getStringExtra("format")
|
||||||
|
return SmsMessage.createFromPdu(pdu, format)
|
||||||
|
}
|
||||||
|
|
||||||
@Deprecated("TODO: Move/rewrite MMS code into the app.")
|
@Deprecated("TODO: Move/rewrite MMS code into the app.")
|
||||||
fun sendMmsMessage(text: String, addresses: List<String>, attachments: List<Attachment>, settings: Settings) {
|
fun sendMmsMessage(text: String, addresses: List<String>, attachments: List<Attachment>, settings: Settings) {
|
||||||
val transaction = Transaction(context, settings)
|
val transaction = Transaction(context, settings)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.app.PendingIntent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.telephony.PhoneNumberUtils
|
import android.telephony.PhoneNumberUtils
|
||||||
|
import com.simplemobiletools.commons.helpers.isSPlus
|
||||||
import com.simplemobiletools.smsmessenger.messaging.SmsException.Companion.EMPTY_DESTINATION_ADDRESS
|
import com.simplemobiletools.smsmessenger.messaging.SmsException.Companion.EMPTY_DESTINATION_ADDRESS
|
||||||
import com.simplemobiletools.smsmessenger.messaging.SmsException.Companion.ERROR_SENDING_MESSAGE
|
import com.simplemobiletools.smsmessenger.messaging.SmsException.Companion.ERROR_SENDING_MESSAGE
|
||||||
import com.simplemobiletools.smsmessenger.receivers.SmsStatusDeliveredReceiver
|
import com.simplemobiletools.smsmessenger.receivers.SmsStatusDeliveredReceiver
|
||||||
|
@ -56,7 +57,10 @@ class SmsSender(val app: Application) {
|
||||||
val deliveryIntents = ArrayList<PendingIntent?>(messageCount)
|
val deliveryIntents = ArrayList<PendingIntent?>(messageCount)
|
||||||
val sentIntents = ArrayList<PendingIntent>(messageCount)
|
val sentIntents = ArrayList<PendingIntent>(messageCount)
|
||||||
|
|
||||||
val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
var flags = PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
if (isSPlus()) {
|
||||||
|
flags = flags or PendingIntent.FLAG_MUTABLE
|
||||||
|
}
|
||||||
|
|
||||||
for (i in 0 until messageCount) {
|
for (i in 0 until messageCount) {
|
||||||
// Make pending intents different for each message part
|
// Make pending intents different for each message part
|
||||||
|
|
|
@ -1,99 +1,96 @@
|
||||||
package com.simplemobiletools.smsmessenger.receivers
|
package com.simplemobiletools.smsmessenger.receivers
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.provider.Telephony.Sms
|
import android.provider.Telephony.Sms
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
import com.simplemobiletools.smsmessenger.extensions.messagesDB
|
import com.simplemobiletools.smsmessenger.extensions.messagesDB
|
||||||
|
import com.simplemobiletools.smsmessenger.extensions.messagingUtils
|
||||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||||
import com.simplemobiletools.smsmessenger.messaging.SendStatusReceiver
|
import com.simplemobiletools.smsmessenger.messaging.SendStatusReceiver
|
||||||
|
|
||||||
/** Handles updating databases and states when a sent SMS message is delivered. */
|
/** Handles updating databases and states when a sent SMS message is delivered. */
|
||||||
class SmsStatusDeliveredReceiver : SendStatusReceiver() {
|
class SmsStatusDeliveredReceiver : SendStatusReceiver() {
|
||||||
|
|
||||||
|
private var status: Int = Sms.Sent.STATUS_NONE
|
||||||
|
|
||||||
override fun updateAndroidDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
|
override fun updateAndroidDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
|
||||||
val uri: Uri? = intent.data
|
val messageUri: Uri? = intent.data
|
||||||
val resultCode = resultCode
|
val smsMessage = context.messagingUtils.getSmsMessageFromDeliveryReport(intent) ?: return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
when (resultCode) {
|
val format = intent.getStringExtra("format")
|
||||||
Activity.RESULT_OK -> {
|
status = smsMessage.status
|
||||||
if (uri != null) {
|
// Simple matching up CDMA status with GSM status.
|
||||||
val values = ContentValues().apply {
|
if ("3gpp2" == format) {
|
||||||
put(Sms.Sent.STATUS, Sms.Sent.STATUS_COMPLETE)
|
val errorClass = status shr 24 and 0x03
|
||||||
put(Sms.Sent.DATE_SENT, System.currentTimeMillis())
|
val statusCode = status shr 16 and 0x3f
|
||||||
put(Sms.Sent.READ, true)
|
status = when (errorClass) {
|
||||||
|
0 -> {
|
||||||
|
if (statusCode == 0x02 /*STATUS_DELIVERED*/) {
|
||||||
|
Sms.STATUS_COMPLETE
|
||||||
|
} else {
|
||||||
|
Sms.STATUS_PENDING
|
||||||
}
|
}
|
||||||
context.contentResolver.update(uri, values, null, null)
|
|
||||||
} else {
|
|
||||||
updateLatestSmsStatus(context, status = Sms.Sent.STATUS_COMPLETE, read = true, date = System.currentTimeMillis())
|
|
||||||
}
|
}
|
||||||
}
|
2 -> {
|
||||||
Activity.RESULT_CANCELED -> {
|
// TODO: Need to check whether SC still trying to deliver the SMS to destination and will send the report again?
|
||||||
if (uri != null) {
|
Sms.STATUS_PENDING
|
||||||
val values = ContentValues().apply {
|
}
|
||||||
put(Sms.Sent.STATUS, Sms.Sent.STATUS_FAILED)
|
3 -> {
|
||||||
put(Sms.Sent.DATE_SENT, System.currentTimeMillis())
|
Sms.STATUS_FAILED
|
||||||
put(Sms.Sent.READ, true)
|
}
|
||||||
put(Sms.Sent.ERROR_CODE, resultCode)
|
else -> {
|
||||||
}
|
Sms.STATUS_PENDING
|
||||||
context.contentResolver.update(uri, values, null, null)
|
|
||||||
} else {
|
|
||||||
updateLatestSmsStatus(context, status = Sms.Sent.STATUS_FAILED, read = true, errorCode = resultCode)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: NullPointerException) {
|
||||||
e.printStackTrace()
|
// Sometimes, SmsMessage.mWrappedSmsMessage is null causing NPE when we access
|
||||||
|
// the methods on it although the SmsMessage itself is not null.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSmsStatusAndDateSent(context, messageUri, System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
private fun updateSmsStatusAndDateSent(context: Context, messageUri: Uri?, timeSentInMillis: Long = -1L) {
|
||||||
private fun updateLatestSmsStatus(context: Context, status: Int, read: Boolean, date: Long = -1L, errorCode: Int = -1) {
|
val resolver = context.contentResolver
|
||||||
val query = context.contentResolver.query(Sms.Sent.CONTENT_URI, null, null, null, "date desc")
|
val values = ContentValues().apply {
|
||||||
|
if (status != Sms.Sent.STATUS_NONE) {
|
||||||
// mark message as delivered in database
|
|
||||||
if (query!!.moveToFirst()) {
|
|
||||||
val id = query.getString(query.getColumnIndex(Sms.Sent._ID))
|
|
||||||
|
|
||||||
val values = ContentValues().apply {
|
|
||||||
put(Sms.Sent.STATUS, status)
|
put(Sms.Sent.STATUS, status)
|
||||||
put(Sms.Sent.READ, read)
|
}
|
||||||
|
put(Sms.Sent.DATE_SENT, timeSentInMillis)
|
||||||
|
}
|
||||||
|
|
||||||
if (date != -1L) {
|
if (messageUri != null) {
|
||||||
put(Sms.Sent.DATE_SENT, date)
|
resolver.update(messageUri, values, null, null)
|
||||||
}
|
} else {
|
||||||
if (errorCode != -1) {
|
// mark latest sms as delivered, need to check if this is still necessary (or reliable)
|
||||||
put(Sms.Sent.ERROR_CODE, errorCode)
|
val cursor = resolver.query(Sms.Sent.CONTENT_URI, null, null, null, "date desc")
|
||||||
|
cursor?.use {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
@SuppressLint("Range")
|
||||||
|
val id = cursor.getString(cursor.getColumnIndex(Sms.Sent._ID))
|
||||||
|
val selection = "${Sms._ID} = ?"
|
||||||
|
val selectionArgs = arrayOf(id.toString())
|
||||||
|
resolver.update(Sms.Sent.CONTENT_URI, values, selection, selectionArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.contentResolver.update(Sms.Sent.CONTENT_URI, values, "_id=$id", null)
|
|
||||||
}
|
}
|
||||||
query.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAppDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
|
override fun updateAppDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
|
||||||
val uri = intent.data
|
val messageUri: Uri? = intent.data
|
||||||
if (uri != null) {
|
if (messageUri != null) {
|
||||||
val messageId = uri.lastPathSegment?.toLong() ?: 0L
|
val messageId = messageUri.lastPathSegment?.toLong() ?: 0L
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
val status = Sms.Sent.STATUS_COMPLETE
|
if (status != Sms.Sent.STATUS_NONE) {
|
||||||
val updated = context.messagesDB.updateStatus(messageId, status)
|
context.messagesDB.updateStatus(messageId, status)
|
||||||
if (updated == 0) {
|
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
|
||||||
ensureBackgroundThread {
|
|
||||||
context.messagesDB.updateStatus(messageId, status)
|
|
||||||
}
|
|
||||||
}, 2000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshMessages()
|
refreshMessages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue