You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android:双卡检测、SIM路径获取及指定SIM保存联系人实现

双卡设备下保存联系人到指定SIM卡的实现方案

我来帮你搞定这个双卡SIM卡保存联系人的问题,一步步拆解解决方案:

一、判断设备是否支持双卡并获取SIM卡信息

要识别双卡状态,我们需要借助Android的TelephonyManagerSubscriptionInfo类,具体步骤如下:

  1. 申请必要权限
    先在AndroidManifest.xml中声明所需权限(注意不同Android版本的权限差异):

    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!-- Android 12及以上版本需要这个权限获取SIM卡详细信息 -->
    <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
    

    这些都是危险权限,代码里一定要做动态权限申请,否则会直接抛出异常。

  2. 获取可用SIM卡列表
    通过TelephonyManager获取所有有效的SIM卡信息:

    private fun getAvailableSimCards(): List<SubscriptionInfo> {
        val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            // Android 5.1及以上版本支持双卡API
            telephonyManager.subscriptionInfoList ?: emptyList()
        } else {
            // 低于5.1的设备基本不支持双卡,返回空列表
            emptyList()
        }
    }
    

    返回的SubscriptionInfo列表里,每个对象包含了SIM卡的subscriptionId(唯一标识)、displayName(比如“SIM1”“中国电信”)等关键信息,我们可以用这些来判断双卡状态和展示选择选项。

二、构造对应SIM卡的联系人URI

你原来使用的content://icc/adn是默认SIM卡的存储路径,双卡设备下每个SIM卡有专属的URI,标准构造方式如下:

private fun getSimContactUri(subId: Int): Uri {
    // 通过subId构造指定SIM卡的联系人URI
    return Uri.parse("content://icc/adn/subId/$subId")
}

部分国产厂商可能有自定义URI格式(比如content://icc/sim${slotIndex}/adnslotIndex从0开始对应SIM1/SIM2),如果标准路径不生效,可以尝试替换成这种格式。

三、实现SIM选择弹窗并保存联系人

当用户点击“保存联系人”按钮时,先检查SIM卡数量,再根据情况展示选择弹窗,最后执行保存操作:

fun showSimSelectionDialog(number: String, name: String) {
    val simCards = getAvailableSimCards()
    when {
        simCards.isEmpty() -> {
            Toast.makeText(context, "未检测到可用SIM卡", Toast.LENGTH_SHORT).show()
        }
        simCards.size == 1 -> {
            // 单卡设备,直接保存到唯一的SIM卡
            addContactToSpecificSim(number, name, simCards[0].subscriptionId)
        }
        else -> {
            // 双卡设备,弹出选择对话框
            val simNames = simCards.map { 
                it.displayName ?: "SIM${it.slotIndex + 1}" 
            }.toTypedArray()
            
            AlertDialog.Builder(context)
                .setTitle("选择保存的SIM卡")
                .setItems(simNames) { _, which ->
                    val selectedSubId = simCards[which].subscriptionId
                    addContactToSpecificSim(number, name, selectedSubId)
                }
                .show()
        }
    }
}

// 修改你原来的方法,支持指定SIM卡保存
private fun addContactToSpecificSim(number: String, name: String, subId: Int) {
    try {
        val simUri = getSimContactUri(subId)
        val values = ContentValues().apply {
            put("number", number)
            put("tag", name)
            // 部分设备需要额外传入sub_id参数,保险起见加上
            put("sub_id", subId)
        }
        context.contentResolver.insert(simUri, values)
        Toast.makeText(context, "联系人已保存到指定SIM卡", Toast.LENGTH_SHORT).show()
    } catch (e: Exception) {
        e.printStackTrace()
        Toast.makeText(context, "保存失败:${e.message}", Toast.LENGTH_SHORT).show()
    }
}

四、额外注意事项

  • 权限校验:调用任何涉及SIM卡或联系人操作的方法前,一定要确保已经获取到对应的动态权限,否则会抛出SecurityException
  • 兼容性:少数厂商的定制系统可能对SIM卡联系人存储路径做了修改,如果标准URI不生效,可以尝试搜索对应厂商的适配方案。
  • 版本限制:双卡相关API从Android 5.1(API 22)开始正式支持,低于该版本的设备无需处理双卡逻辑。

内容的提问来源于stack exchange,提问作者Manoj Kumar

火山引擎 最新活动