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

Android双卡设备指定SIM卡存储联系人的实现方案咨询

解决方案:指定SIM卡存储联系人

我之前也碰到过这个问题,Android上指定SIM卡存储联系人确实因为厂商差异有点麻烦,不过可以通过以下方案解决:

核心原理

Android系统中,每张SIM卡的联系人存储URI是独立的,并非统一的content://icc/adn。我们需要先获取目标SIM卡的订阅信息(比如subscriptionIdiccId),再构建对应SIM卡的专属URI,最后用这个URI插入联系人。

步骤1:添加必要权限

首先在AndroidManifest.xml中声明权限:

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

注意:Android 6.0及以上版本需要动态申请这两个权限,否则无法获取SIM卡信息和写入联系人。

步骤2:获取SIM卡订阅信息

通过TelephonyManager获取设备上的活跃SIM卡列表,这样可以让用户选择要存储的SIM卡(比如根据卡槽ID、运营商名称区分):

private fun getActiveSimList(): 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及以上支持获取订阅信息
        telephonyManager.activeSubscriptionInfoList
    } else {
        // 低于5.1的版本兼容性较差,建议提示用户无法指定SIM卡
        null
    }
}

你可以把这个列表展示给用户,让他们选择SIM1或SIM2,记录对应的slotIndex(卡槽ID,通常0是SIM1,1是SIM2)。

步骤3:构建目标SIM卡的联系人URI

不同厂商的SIM卡URI格式可能不同,我们需要尝试几种常见格式,找到可写入的URI:

private fun getTargetSimUri(selectedSlotId: Int): Uri? {
    val simList = getActiveSimList() ?: return null
    val targetSim = simList.find { it.slotIndex == selectedSlotId } ?: return null

    // 常见的SIM卡联系人URI格式,按优先级尝试
    val possibleUris = listOf(
        "content://icc/adn/subId/${targetSim.subscriptionId}",
        "content://icc/adn/${targetSim.iccId}",
        "content://sim/adn/subId/${targetSim.subscriptionId}",
        "content://sim/adn/${targetSim.iccId}"
    )

    // 验证URI是否可写入(插入测试数据后立即删除)
    for (uriStr in possibleUris) {
        val uri = Uri.parse(uriStr)
        try {
            val testUri = context.contentResolver.insert(uri, ContentValues().apply {
                put("tag", "temp_test")
                put("number", "12345")
            })
            testUri?.let {
                context.contentResolver.delete(it, null, null)
                return uri
            }
        } catch (e: Exception) {
            // 当前URI不可用,继续尝试下一个
        }
    }
    return null
}

步骤4:修改存储函数,支持指定SIM卡

更新你原来的函数,传入用户选择的卡槽ID,使用对应的URI存储联系人:

private fun addContactToSim(number: String, name: String, selectedSlotId: Int) {
    try {
        val simUri = getTargetSimUri(selectedSlotId) ?: run {
            Log.e("SimContact", "无法找到目标SIM卡的有效存储URI")
            return
        }

        val cv = ContentValues().apply {
            put("tag", name) // SIM卡联系人的姓名字段
            put("number", number) // SIM卡联系人的号码字段
            // 注意:SIM卡通常不支持邮箱字段,插入后可能被忽略,建议移除
            // put("email", "Email")
        }

        context.contentResolver.insert(simUri, cv)?.let {
            context.contentResolver.notifyChange(simUri, null)
            Log.d("SimContact", "联系人已成功存入指定SIM卡")
        } ?: Log.e("SimContact", "插入联系人失败")
    } catch (e: Exception) {
        e.printStackTrace()
        Log.e("SimContact", "存储联系人出错:${e.message}")
    }
}

注意事项

  • 字段限制:绝大多数SIM卡仅支持姓名(tag)和号码(number)字段,邮箱、地址等字段无法存储,插入后会被忽略。
  • 兼容性:部分厂商可能限制第三方应用写入SIM卡,此时即使代码正确也无法完成操作,需要提示用户。
  • 旧版本兼容:Android 5.1以下版本没有统一的SIM卡订阅API,很难实现指定SIM卡存储,建议针对这些版本做降级处理(比如默认存储到默认SIM卡)。

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

火山引擎 最新活动