Here's a full sample, showing how to choose one, and tested on real device (OnePlus 2) with 2 sim cards. Note that you should handle permissions (grant them before using the code) :
manifest
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- <uses-permission android:name="android.permission.READ_SMS" />-->
<!-- <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />-->
build.gradle
minSdkVersion 21
targetSdkVersion 29
...
implementation "com.google.android.gms:play-services-auth:17.0.0"
activity_main.xml
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:text="send sms" />
MainActivity.kt
import android.annotation.SuppressLint
import android.app.Activity
import android.app.PendingIntent
import android.content.*
import android.os.Build
import android.os.Bundle
import android.telephony.SmsManager
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
import android.util.LongSparseArray
import android.view.View
import android.view.autofill.AutofillManager
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.auth.api.Auth
import com.google.android.gms.auth.api.credentials.Credential
import com.google.android.gms.auth.api.credentials.HintRequest
import com.google.android.gms.common.api.GoogleApiClient
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private lateinit var googleApiClient: GoogleApiClient
private val partialSmsIdToSmsReceiverMap = LongSparseArray<SmsBroadcastReceiver>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.visibility = View.GONE
tryGetCurrentUserPhoneNumber(this)
googleApiClient = GoogleApiClient.Builder(this).addApi(Auth.CREDENTIALS_API).build()
if (phoneNumberToSendTo.isEmpty()) {
val hintRequest = HintRequest.Builder().setPhoneNumberIdentifierSupported(true).build()
val intent = Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest)
try {
startIntentSenderForResult(intent.intentSender, REQUEST_PHONE_NUMBER, null, 0, 0, 0);
} catch (e: IntentSender.SendIntentException) {
Toast.makeText(this, "failed to show phone picker", Toast.LENGTH_SHORT).show()
}
} else
onGotPhoneNumberToSendTo(phoneNumberToSendTo)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_PHONE_NUMBER) {
if (resultCode == Activity.RESULT_OK) {
val cred: Credential? = data?.getParcelableExtra(Credential.EXTRA_KEY)
phoneNumberToSendTo = cred?.id ?: ""
if (phoneNumberToSendTo.isEmpty())
Toast.makeText(this, "failed to get phone number", Toast.LENGTH_SHORT).show()
else
onGotPhoneNumberToSendTo(phoneNumberToSendTo)
}
}
}
private fun onGotPhoneNumberToSendTo(normalizedPhoneNumberToSendSmsTo: String) {
button.visibility = View.VISIBLE
button.text = "send SMS to $normalizedPhoneNumberToSendSmsTo"
button.setOnClickListener {
val smsManager = SmsManager.getDefault()
val messageToSend = "Hello there"
val parts = smsManager.divideMessage(messageToSend)
val sentIntents = ArrayList<PendingIntent>(parts.size)
val fullSmsMessageId = fullSmsIdCounter++
Log.d("AppLog", " sendSmsMessage sending sms #$fullSmsMessageId parts count:${parts.size} messageToSend:\n$messageToSend")
val pendingPartialSmsIds = HashSet<Long>()
for (i in 0 until parts.size) {
val partialSmsId = partialSmsIdCounter++
Log.d("AppLog", " sendSmsMessage sending sms #$fullSmsMessageId part id:${partialSmsId}")
val action = "$ACTION_SMS_SENT_FORMAT$partialSmsId"
sentIntents.add(PendingIntent.getBroadcast(applicationContext, 0, Intent(action), 0))
val smsSentBroadcastReceiver = SmsBroadcastReceiver(fullSmsMessageId, partialSmsId)
partialSmsIdToSmsReceiverMap.put(partialSmsId, smsSentBroadcastReceiver)
applicationContext.registerReceiver(smsSentBroadcastReceiver, IntentFilter(action))
pendingPartialSmsIds.add(partialSmsId)
}
sendSmsUsingDefaultSimCard(applicationContext, normalizedPhoneNumberToSendSmsTo, parts, sentIntents)
}
}
@SuppressLint("NewApi", "MissingPermission")
fun sendSmsUsingDefaultSimCard(applicationContext: Context, destinationAddress: String, parts: ArrayList<String>,
sentIntents: ArrayList<PendingIntent>? = null, deliveryIntents: ArrayList<PendingIntent>? = null) {
val defaultSmsManager = SmsManager.getDefault()
val phoneNumber = if (destinationAddress.startsWith("+")) destinationAddress else "+$destinationAddress"
//check if we have multi-SIM and don't have a default one to work with, so that we will choose it ourselves
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
defaultSmsManager.sendMultipartTextMessage(phoneNumber, null, parts, sentIntents, deliveryIntents)
return
}
val subscriptionManager = applicationContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
val defaultSubscriptionId = SmsManager.getDefaultSmsSubscriptionId()
val smsManager = SmsManager.getSmsManagerForSubscriptionId(defaultSubscriptionId)
if (smsManager != null) {
smsManager.sendMultipartTextMessage(phoneNumber, null, parts, sentIntents, deliveryIntents)
return
}
val activeSubscriptionInfoList: MutableList<SubscriptionInfo>? = subscriptionManager.activeSubscriptionInfoList
val subscriptionInfoId = activeSubscriptionInfoList?.getOrNull(0)?.subscriptionId
if (subscriptionInfoId != null)
SmsManager.getSmsManagerForSubscriptionId(subscriptionInfoId).sendMultipartTextMessage(phoneNumber, null, parts, sentIntents, deliveryIntents)
else
defaultSmsManager.sendMultipartTextMessage(phoneNumber, null, parts, sentIntents, deliveryIntents)
}
private inner class SmsBroadcastReceiver(val fullSmsId: Long, val partialSmsId: Long) : BroadcastReceiver() {
override fun onReceive(someContext: Context, intent: Intent) {
Log.d("AppLog", " SmsBroadcastReceiver onReceive")
applicationContext.unregisterReceiver(this)
partialSmsIdToSmsReceiverMap.remove(partialSmsId)
val smsError: String? = when (resultCode) {
-1, 0 /*SmsManager.RESULT_ERROR_NONE*/ -> null
SmsManager.RESULT_ERROR_GENERIC_FAILURE -> "RESULT_ERROR_GENERIC_FAILURE"
SmsManager.RESULT_ERROR_RADIO_OFF -> "RESULT_ERROR_RADIO_OFF"
SmsManager.RESULT_ERROR_NULL_PDU -> "RESULT_ERROR_NULL_PDU"
SmsManager.RESULT_ERROR_NO_SERVICE -> "RESULT_ERROR_NO_SERVICE"
SmsManager.RESULT_ERROR_LIMIT_EXCEEDED -> "RESULT_ERROR_LIMIT_EXCEEDED"
SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED -> "RESULT_ERROR_SHORT_CODE_NOT_ALLOWED"
SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED -> "RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED"
/**SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE*/
6 -> "RESULT_ERROR_FDN_CHECK_FAILURE"
// 16 /*SmsManager.RESULT_MODEM_ERROR*/ -> "RESULT_MODEM_ERROR"
// 111 /*SmsManager.RESULT_RIL_MODEM_ERR*/ -> "RESULT_RIL_MODEM_ERR"
else -> "Unknown error"
}
Log.d("AppLog", "SmsBroadcastReceiver sms #$fullSmsId part #$partialSmsId send-state updated. sent fine?${smsError == null} (error:$smsError) errorCode:$resultCode")
}
}
companion object {
private const val REQUEST_PHONE_NUMBER = 1
private var partialSmsIdCounter = 0L
private var fullSmsIdCounter = 0L
private const val ACTION_SMS_SENT_FORMAT = "_ACTION_SENT_"
private var phoneNumberToSendTo = ""
@SuppressLint("MissingPermission", "HardwareIds")
private fun tryGetCurrentUserPhoneNumber(context: Context): String {
if (phoneNumberToSendTo.isNotEmpty())
return phoneNumberToSendTo
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val subscriptionManager = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
try {
subscriptionManager.activeSubscriptionInfoList?.forEach {
val number: String? = it.number
if (!number.isNullOrBlank()) {
phoneNumberToSendTo = number
return number
}
}
} catch (ignored: Exception) {
}
}
try {
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val number = telephonyManager.line1Number ?: ""
if (!number.isBlank()) {
phoneNumberToSendTo = number
return number
}
} catch (e: Exception) {
}
return ""
}
}
}