Improve third party SMS/MMS intent parsing
Closes https://github.com/FossifyOrg/Messages/issues/243 Closes https://github.com/FossifyOrg/Messages/issues/217
This commit is contained in:
parent
f66aa77831
commit
4434d187bc
3 changed files with 152 additions and 17 deletions
|
|
@ -80,7 +80,6 @@
|
|||
android:parentActivityName=".activities.MainActivity">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.SENDTO" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,31 @@ import android.widget.Toast
|
|||
import com.google.gson.Gson
|
||||
import com.reddit.indicatorfastscroll.FastScrollItemIndicator
|
||||
import org.fossify.commons.dialogs.RadioGroupDialog
|
||||
import org.fossify.commons.extensions.*
|
||||
import org.fossify.commons.helpers.*
|
||||
import org.fossify.commons.extensions.applyColorFilter
|
||||
import org.fossify.commons.extensions.areSystemAnimationsEnabled
|
||||
import org.fossify.commons.extensions.beGone
|
||||
import org.fossify.commons.extensions.beVisible
|
||||
import org.fossify.commons.extensions.beVisibleIf
|
||||
import org.fossify.commons.extensions.getColorStateList
|
||||
import org.fossify.commons.extensions.getContrastColor
|
||||
import org.fossify.commons.extensions.getMyContactsCursor
|
||||
import org.fossify.commons.extensions.getPhoneNumberTypeText
|
||||
import org.fossify.commons.extensions.getProperPrimaryColor
|
||||
import org.fossify.commons.extensions.getProperTextColor
|
||||
import org.fossify.commons.extensions.hasPermission
|
||||
import org.fossify.commons.extensions.hideKeyboard
|
||||
import org.fossify.commons.extensions.normalizeString
|
||||
import org.fossify.commons.extensions.onTextChangeListener
|
||||
import org.fossify.commons.extensions.toast
|
||||
import org.fossify.commons.extensions.underlineText
|
||||
import org.fossify.commons.extensions.updateTextColors
|
||||
import org.fossify.commons.extensions.value
|
||||
import org.fossify.commons.extensions.viewBinding
|
||||
import org.fossify.commons.helpers.MyContactsContentProvider
|
||||
import org.fossify.commons.helpers.NavigationIcon
|
||||
import org.fossify.commons.helpers.PERMISSION_READ_CONTACTS
|
||||
import org.fossify.commons.helpers.SimpleContactsHelper
|
||||
import org.fossify.commons.helpers.ensureBackgroundThread
|
||||
import org.fossify.commons.models.RadioItem
|
||||
import org.fossify.commons.models.SimpleContact
|
||||
import org.fossify.messages.R
|
||||
|
|
@ -18,7 +41,13 @@ import org.fossify.messages.databinding.ActivityNewConversationBinding
|
|||
import org.fossify.messages.databinding.ItemSuggestedContactBinding
|
||||
import org.fossify.messages.extensions.getSuggestedContacts
|
||||
import org.fossify.messages.extensions.getThreadId
|
||||
import org.fossify.messages.helpers.*
|
||||
import org.fossify.messages.helpers.SmsIntentParser
|
||||
import org.fossify.messages.helpers.THREAD_ATTACHMENT_URI
|
||||
import org.fossify.messages.helpers.THREAD_ATTACHMENT_URIS
|
||||
import org.fossify.messages.helpers.THREAD_ID
|
||||
import org.fossify.messages.helpers.THREAD_NUMBER
|
||||
import org.fossify.messages.helpers.THREAD_TEXT
|
||||
import org.fossify.messages.helpers.THREAD_TITLE
|
||||
import org.fossify.messages.messaging.isShortCodeWithLetters
|
||||
import java.net.URLDecoder
|
||||
import java.util.Locale
|
||||
|
|
@ -42,7 +71,10 @@ class NewConversationActivity : SimpleActivity() {
|
|||
useTransparentNavigation = true,
|
||||
useTopSearchMenu = false
|
||||
)
|
||||
setupMaterialScrollListener(scrollingView = binding.contactsList, toolbar = binding.newConversationToolbar)
|
||||
setupMaterialScrollListener(
|
||||
scrollingView = binding.contactsList,
|
||||
toolbar = binding.newConversationToolbar
|
||||
)
|
||||
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
binding.newConversationAddress.requestFocus()
|
||||
|
|
@ -112,9 +144,14 @@ class NewConversationActivity : SimpleActivity() {
|
|||
}
|
||||
|
||||
private fun isThirdPartyIntent(): Boolean {
|
||||
if ((intent.action == Intent.ACTION_SENDTO || intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_VIEW) && intent.dataString != null) {
|
||||
val number = intent.dataString!!.removePrefix("sms:").removePrefix("smsto:").removePrefix("mms").removePrefix("mmsto:").replace("+", "%2b").trim()
|
||||
launchThreadActivity(URLDecoder.decode(number), "")
|
||||
val result = SmsIntentParser.parse(intent)
|
||||
if (result != null) {
|
||||
val (body, recipients) = result
|
||||
launchThreadActivity(
|
||||
phoneNumber = URLDecoder.decode(recipients),
|
||||
name = "",
|
||||
body = body
|
||||
)
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
|
|
@ -142,7 +179,11 @@ class NewConversationActivity : SimpleActivity() {
|
|||
val hasContacts = contacts.isNotEmpty()
|
||||
binding.contactsList.beVisibleIf(hasContacts)
|
||||
binding.noContactsPlaceholder.beVisibleIf(!hasContacts)
|
||||
binding.noContactsPlaceholder2.beVisibleIf(!hasContacts && !hasPermission(PERMISSION_READ_CONTACTS))
|
||||
binding.noContactsPlaceholder2.beVisibleIf(
|
||||
!hasContacts && !hasPermission(
|
||||
PERMISSION_READ_CONTACTS
|
||||
)
|
||||
)
|
||||
|
||||
if (!hasContacts) {
|
||||
val placeholderText = if (hasPermission(PERMISSION_READ_CONTACTS)) {
|
||||
|
|
@ -168,7 +209,13 @@ class NewConversationActivity : SimpleActivity() {
|
|||
val items = ArrayList<RadioItem>()
|
||||
phoneNumbers.forEachIndexed { index, phoneNumber ->
|
||||
val type = getPhoneNumberTypeText(phoneNumber.type, phoneNumber.label)
|
||||
items.add(RadioItem(index, "${phoneNumber.normalizedNumber} ($type)", phoneNumber.normalizedNumber))
|
||||
items.add(
|
||||
RadioItem(
|
||||
index,
|
||||
"${phoneNumber.normalizedNumber} ($type)",
|
||||
phoneNumber.normalizedNumber
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
RadioGroupDialog(this, items) {
|
||||
|
|
@ -212,10 +259,17 @@ class NewConversationActivity : SimpleActivity() {
|
|||
suggestedContactName.setTextColor(getProperTextColor())
|
||||
|
||||
if (!isDestroyed) {
|
||||
SimpleContactsHelper(this@NewConversationActivity).loadContactImage(contact.photoUri, suggestedContactImage, contact.name)
|
||||
SimpleContactsHelper(this@NewConversationActivity).loadContactImage(
|
||||
contact.photoUri,
|
||||
suggestedContactImage,
|
||||
contact.name
|
||||
)
|
||||
binding.suggestionsHolder.addView(root)
|
||||
root.setOnClickListener {
|
||||
launchThreadActivity(contact.phoneNumbers.first().normalizedNumber, contact.name)
|
||||
launchThreadActivity(
|
||||
contact.phoneNumbers.first().normalizedNumber,
|
||||
contact.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -231,28 +285,32 @@ class NewConversationActivity : SimpleActivity() {
|
|||
try {
|
||||
val name = contacts[position].name
|
||||
val character = if (name.isNotEmpty()) name.substring(0, 1) else ""
|
||||
FastScrollItemIndicator.Text(character.uppercase(Locale.getDefault()).normalizeString())
|
||||
FastScrollItemIndicator.Text(
|
||||
character.uppercase(Locale.getDefault()).normalizeString()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
FastScrollItemIndicator.Text("")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun launchThreadActivity(phoneNumber: String, name: String) {
|
||||
private fun launchThreadActivity(phoneNumber: String, name: String, body: String = "") {
|
||||
hideKeyboard()
|
||||
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: intent.getStringExtra("sms_body") ?: ""
|
||||
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))
|
||||
putExtra(THREAD_TITLE, name)
|
||||
putExtra(THREAD_TEXT, text)
|
||||
putExtra(THREAD_TEXT, body)
|
||||
putExtra(THREAD_NUMBER, number)
|
||||
|
||||
if (intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
|
||||
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
putExtra(THREAD_ATTACHMENT_URI, uri?.toString())
|
||||
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
|
||||
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE && intent.extras?.containsKey(
|
||||
Intent.EXTRA_STREAM
|
||||
) == true
|
||||
) {
|
||||
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
putExtra(THREAD_ATTACHMENT_URIS, uris)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
package org.fossify.messages.helpers
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import com.google.android.mms.ContentType
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLDecoder
|
||||
|
||||
// Base on https://cs.android.com/android/platform/superproject/main/+/main:packages/apps/Messaging/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java
|
||||
object SmsIntentParser {
|
||||
private const val SCHEME_SMS = "sms"
|
||||
private const val SCHEME_SMSTO = "smsto"
|
||||
private const val SCHEME_MMS = "mms"
|
||||
private const val SCHEME_MMSTO = "mmsto"
|
||||
private val SMS_MMS_SCHEMES = setOf(SCHEME_SMS, SCHEME_SMSTO, SCHEME_MMS, SCHEME_MMSTO)
|
||||
|
||||
private const val MAX_RECIPIENT_LENGTH = 100
|
||||
private const val SMS_BODY = "sms_body"
|
||||
private const val ADDRESS = "address"
|
||||
|
||||
fun parse(intent: Intent): Pair<String, String>? {
|
||||
val action = intent.action
|
||||
if (action != Intent.ACTION_SENDTO && action != Intent.ACTION_VIEW) {
|
||||
// Unsupported intent action
|
||||
return null
|
||||
}
|
||||
|
||||
val recipients = parseRecipients(intent)
|
||||
val body = extractBodyFromIntent(intent)
|
||||
return body.orEmpty() to recipients
|
||||
}
|
||||
|
||||
private fun parseRecipients(intent: Intent): String {
|
||||
val uriRecipients = parseRecipientsFromUri(intent.data)
|
||||
val extraAddress = intent.getStringExtra(ADDRESS)
|
||||
val extraEmail = intent.getStringExtra(Intent.EXTRA_EMAIL)
|
||||
|
||||
val recipients = when {
|
||||
!extraAddress.isNullOrEmpty() -> arrayOf(extraAddress)
|
||||
!extraEmail.isNullOrEmpty() -> arrayOf(extraEmail)
|
||||
else -> uriRecipients.orEmpty()
|
||||
}
|
||||
|
||||
return recipients
|
||||
.filter { it.length < MAX_RECIPIENT_LENGTH }
|
||||
.joinToString(";")
|
||||
}
|
||||
|
||||
private fun parseRecipientsFromUri(uri: Uri?): Array<String>? {
|
||||
if (uri == null || uri.scheme !in SMS_MMS_SCHEMES) return null
|
||||
val schemeSpecificPart = uri.schemeSpecificPart.split("?").firstOrNull() ?: return null
|
||||
return schemeSpecificPart.replace(';', ',').split(",").toTypedArray()
|
||||
}
|
||||
|
||||
private fun extractBodyFromIntent(intent: Intent): String? {
|
||||
val uriBody = extractBodyFromUri(intent.data)
|
||||
val smsBody = intent.getStringExtra(SMS_BODY)
|
||||
val extraText = if (ContentType.TEXT_PLAIN == intent.type) {
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||
} else {
|
||||
// Invalid URL, probably
|
||||
null
|
||||
}
|
||||
|
||||
return smsBody ?: uriBody ?: extraText
|
||||
}
|
||||
|
||||
private fun extractBodyFromUri(uri: Uri?): String? {
|
||||
if (uri == null) return null
|
||||
val query = uri.query ?: return null
|
||||
val bodyParam = query.split("&").firstOrNull { it.startsWith("body=") } ?: return null
|
||||
return try {
|
||||
URLDecoder.decode(bodyParam.removePrefix("body="), "UTF-8")
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue