【问题标题】:Option to send sms using Sim1 or Sim2 programmatically以编程方式使用 Sim1 或 Sim2 发送短信的选项
【发布时间】:2014-02-20 00:27:30
【问题描述】:

我有一部带 2 张 SIM 卡的 Android 手机,我想使用 Sim1 或 Sim2 发送短信。默认情况下,短信是从 sim1 发送的。但我想从 sim2 发送短信。是否可以设置使用 Sim1 或 Sim2 发送短信?

如果有使用 Sim1 或 Sim2 发送短信的设置选项,那就太好了。这对于双 SIM 卡 Android 手机很有用。 我创建了短信应用程序 android 我已经能够顺利发送短信应用程序,但默认短信由 SIM 1 发送。但我想通过设置通过 sim1 或 sim2 发送短信以编程方式发送短信?

【问题讨论】:

  • AFAIK 没有标准的方法可以做到这一点,因为 Android SDK 不支持双 SIM 卡。然而,涉及反射的黑客可能会奏效——关于 SO 已经有很多类似的问题了!
  • 嗨,Jeba,你有解决方案吗?如果你有请帮助我
  • @GangadharNimbally,您好,目前还没有任何解决方案……我也在寻找解决方案。
  • 你可以在这里找到详细的答案stackoverflow.com/questions/27351936/…

标签: android sms sim-card smsmanager


【解决方案1】:

这是一个完整的示例,展示了如何选择一个,并在带有 2 个 SIM 卡的真实设备 (OnePlus 2) 上进行了测试。请注意,您应该处理权限(在使用代码之前授予它们):

清单

    <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 ""
        }
    }
}


【讨论】:

    【解决方案2】:

    没有 android.permission.READ_PHONE_STATE 的解决方案

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
            SubscriptionManager subs = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
            if (subs != null) {
                Log.d("sim_spalsh", "num sims = " + subs.getActiveSubscriptionInfoCountMax());
    
                if (subs.getActiveSubscriptionInfoCountMax() > 1) {
    
                    //SendSMS From SIM One
                    SmsManager.getSmsManagerForSubscriptionId(0)
                            .sendTextMessage(phonenumber, null, "sim1", null, null);
    
                    //SendSMS From SIM Two
                    SmsManager.getSmsManagerForSubscriptionId(1)
                            .sendTextMessage(phonenumber, null, "sim2", null, null);
    
                }
            }
        }
    

    您必须保证&lt;uses-permission android:name="android.permission.SEND_SMS"/&gt;

    【讨论】:

    • 在 Android API 22 之前有解决方案吗?此外,如果 sim 卡超过 1 张,尝试以正常方式(使用 sendMultipartTextMessage)发送 SMS 的应用程序会崩溃吗?
    【解决方案3】:

    如果您可以将此代码用于 API 级别 22+。

    private void sendDirectSMS() {
    
        private static String SENT = "SMS_SENT", DELIVERED = "SMS_DELIVERED";
    
        PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, new Intent(
            SENT), 0);
    
        PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,
            new Intent(DELIVERED), 0);
    
        // SEND BroadcastReceiver
        BroadcastReceiver sendSMS = new BroadcastReceiver() {
            @Override
            public void onReceive(Context arg0, Intent arg1) {
                switch (getResultCode()) {
                    case Activity.RESULT_OK:
                        showSnackBar(getString(R.string.sms_sent));
                        Analytics.track(AnalyticsEvents.SEND_REMINDER_SMS_APP_SUCCESS);
                        break;
                    case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                        showSnackBar(getString(R.string.sms_send_failed_try_again));
                        Analytics.track(AnalyticsEvents.SEND_REMINDER_SMS_APP_FAILED);
                        break;
                    case SmsManager.RESULT_ERROR_NO_SERVICE:
                        showSnackBar(getString(R.string.no_service_sms_failed));
                        Analytics.track(AnalyticsEvents.SEND_REMINDER_SMS_APP_FAILED);
                        break;
                    case SmsManager.RESULT_ERROR_NULL_PDU:
                        showSnackBar(getString(R.string.no_service_sms_failed));
                        Analytics.track(AnalyticsEvents.SEND_REMINDER_SMS_APP_FAILED);
                        break;
                    case SmsManager.RESULT_ERROR_RADIO_OFF:
                        showSnackBar(getString(R.string.no_service_sms_failed));
                        Analytics.track(AnalyticsEvents.SEND_REMINDER_SMS_APP_FAILED);
                        break;
                }
            }
        };
    
        // DELIVERY BroadcastReceiver
        BroadcastReceiver deliverSMS = new BroadcastReceiver() {
            @Override
            public void onReceive(Context arg0, Intent arg1) {
                switch (getResultCode()) {
                    case Activity.RESULT_OK:
                        Toast.makeText(getBaseContext(), R.string.sms_delivered,
                            Toast.LENGTH_SHORT).show();
                        break;
                    case Activity.RESULT_CANCELED:
                        Toast.makeText(getBaseContext(), R.string.sms_not_delivered,
                            Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        };
    
        registerReceiver(sendSMS, new IntentFilter(SENT));
        registerReceiver(deliverSMS, new IntentFilter(DELIVERED));
        String smsText = getSmsText();
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            SubscriptionManager localSubscriptionManager = SubscriptionManager.from(context);
            if (localSubscriptionManager.getActiveSubscriptionInfoCount() > 1) {
                List localList = localSubscriptionManager.getActiveSubscriptionInfoList();
    
                SubscriptionInfo simInfo1 = (SubscriptionInfo) localList.get(0);
                SubscriptionInfo simInfo2 = (SubscriptionInfo) localList.get(1);
    
                //SendSMS From SIM One
                SmsManager.getSmsManagerForSubscriptionId(simInfo1.getSubscriptionId()).sendTextMessage(customer.getMobile(), null, smsText, sentPI, deliveredPI);
    
                //SendSMS From SIM Two
                SmsManager.getSmsManagerForSubscriptionId(simInfo2.getSubscriptionId()).sendTextMessage(customer.getMobile(), null, smsText, sentPI, deliveredPI);
            }
        } else {
            SmsManager.getDefault().sendTextMessage(customer.getMobile(), null, smsText, sentPI, deliveredPI);
            Toast.makeText(getBaseContext(), R.string.sms_sending, Toast.LENGTH_SHORT).show();
        }
    }
    

    同时添加权限。

    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    

    【讨论】:

    • 什么 SENT & DELIVERED 变量值?以及 sendTextMessage(customer.getMobile(),... 行中的“客户”是什么?
    • @asqa我已经用 SENT & DELIVERED 值编辑了答案,customer.getMobile() 是手机号码
    • 我强烈推荐这个更清洁的解决方案(无需反射,适用于 API 级别 22+)
    • 显示异常“异常:java.lang.RuntimeException:无法获得名为'isms0'的服务在设备中不起作用”
    • 开发者应该如何保存选择的subscriptionInfo,以便下次用户选择发送短信时选择它?那里有一些唯一的ID吗? iccid?电话号码(有用,因为这是发送的那个)?如果没有为 SMS 选择默认 SIM 卡(通过 subscriptionId
    【解决方案4】:
    SmsManager.getSmsManagerForSubscriptionId(int subscriptionId).sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, PendingIntent deliveryIntent);
    

    这可以在 5.1 Lollipop 版本之后工作。在此之前我们需要看到更多。一旦我得到答案,我会更新。

    【讨论】:

    • 那么如何从 sim1 或 sim2 发送?
    【解决方案5】:

    该选项在 Android 设置中可用 - 导航至设置>>无线和网络>>SIM 管理>>默认设置>>消息>>并选择“总是询问”。

    【讨论】:

      【解决方案6】:

      您应该查看 tasker,您可以使用它来为此构建一个切换小部件。

      【讨论】:

        猜你喜欢
        • 2011-04-15
        • 1970-01-01
        • 2014-05-09
        • 2012-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-06
        • 1970-01-01
        相关资源
        最近更新 更多