You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取可保存联系人的合规账户及多SIM卡账户数据?

解决方案:合规联系人存储账户获取与多SIM卡支持

一、获取用户预期的合规联系人存储账户

你之前使用ContentResolver.getSyncAdapterTypes()得到大量无关账户(如Telegram、Duo),原因是这类IM应用的SyncAdapter也注册了ContactsContract.AUTHORITY用于同步自身联系人,但并非用户预期的“联系人存储账户”。以下是无硬编码的通用筛选方案:

核心思路

  1. 先获取所有可同步联系人的账户(通过ContactsContract.Settings筛选已启用同步的账户)
  2. 再过滤掉非系统级/非联系人专属的账户:通过PackageManager判断账户类型对应的应用是否为系统应用或联系人相关应用,排除IM类第三方应用。

代码实现

import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.provider.ContactsContract
import android.accounts.Account
import android.accounts.AccountManager

fun getValidContactAccounts(context: Context): List<Account> {
    val validAccounts = mutableListOf<Account>()
    val contentResolver = context.contentResolver
    val packageManager = context.packageManager

    // 步骤1:获取所有启用联系人同步的账户
    val projection = arrayOf(
        ContactsContract.Settings.ACCOUNT_NAME,
        ContactsContract.Settings.ACCOUNT_TYPE
    )
    val selection = "${ContactsContract.Settings.SYNC_ENABLED} = ?"
    val selectionArgs = arrayOf("1")

    contentResolver.query(
        ContactsContract.Settings.CONTENT_URI,
        projection,
        selection,
        selectionArgs,
        null
    )?.use { cursor ->
        while (cursor.moveToNext()) {
            val accountName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Settings.ACCOUNT_NAME))
            val accountType = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Settings.ACCOUNT_TYPE))
            val account = Account(accountName, accountType)
            if (!validAccounts.contains(account)) {
                validAccounts.add(account)
            }
        }
    }

    // 添加本机存储账户(账户名和类型均为null)
    validAccounts.add(Account(null, null))

    // 步骤2:过滤非预期账户,仅保留系统/联系人相关应用的账户
    return validAccounts.filter { account ->
        // 本机账户直接保留
        account.type.isNullOrBlank() || try {
            val appInfo = packageManager.getApplicationInfo(account.type, 0)
            // 判断是否为系统应用,或应用包名包含联系人相关关键词
            (appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) ||
            appInfo.packageName.contains("contact", ignoreCase = true) ||
            appInfo.packageName.contains("google", ignoreCase = true) ||
            appInfo.packageName.contains("exchange", ignoreCase = true)
        } catch (e: PackageManager.NameNotFoundException) {
            // 无法找到对应应用的账户直接排除
            false
        }
    }
}

二、正确获取多SIM卡账户数据

硬编码SIM卡账户类型(如com.android.contacts.sim)无法适配不同厂商,以下方案通过系统API动态识别所有可用SIM卡账户:

核心思路

  1. 先从AccountManager中筛选所有账户类型包含sim关键词的账户(覆盖不同厂商的SIM账户类型)
  2. 结合TelephonyManager获取多SIM卡槽的激活状态,完善SIM账户的名称与对应关系,确保所有可用SIM卡都被识别。

代码实现

import android.content.Context
import android.accounts.Account
import android.accounts.AccountManager
import android.telephony.TelephonyManager
import android.os.Build

fun getSimContactAccounts(context: Context): List<Account> {
    val simAccounts = mutableListOf<Account>()
    val accountManager = AccountManager.get(context)
    val allAccounts = accountManager.getAccounts().toList()
    val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager

    // 步骤1:筛选所有类型含"sim"的账户
    simAccounts.addAll(allAccounts.filter { account ->
        account.type.lowercase().contains("sim")
    })

    // 步骤2:结合TelephonyManager验证多SIM卡状态,补充遗漏的SIM账户
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        val simCount = telephonyManager.activeModemCount
        for (slotId in 0 until simCount) {
            if (telephonyManager.getSimState(slotId) == TelephonyManager.SIM_STATE_READY) {
                val carrierName = telephonyManager.getCarrierName(slotId)?.toString() ?: "SIM ${slotId + 1}"
                // 匹配对应卡槽的SIM账户
                val matchedAccount = allAccounts.find { account ->
                    account.type.lowercase().contains("sim") &&
                    (account.name.contains(carrierName, ignoreCase = true) || account.name.contains("${slotId + 1}"))
                }
                matchedAccount?.let {
                    if (!simAccounts.contains(it)) simAccounts.add(it)
                }
            }
        }
    } else {
        // 适配Android 11及以下版本
        val simStates = telephonyManager.simStates ?: return simAccounts
        simStates.forEachIndexed { slotId, simState ->
            if (simState == TelephonyManager.SIM_STATE_READY) {
                val carrierName = telephonyManager.networkOperatorName ?: "SIM ${slotId + 1}"
                val matchedAccount = allAccounts.find { account ->
                    account.type.lowercase().contains("sim") &&
                    account.name.contains(carrierName, ignoreCase = true)
                }
                matchedAccount?.let {
                    if (!simAccounts.contains(it)) simAccounts.add(it)
                }
            }
        }
    }

    return simAccounts.distinct()
}

保存联系人到SIM卡的注意事项

当用户选择保存到某张SIM卡时,直接使用上述方法获取的Account对象的name和type,设置到RawContacts的ACCOUNT_NAME和ACCOUNT_TYPE字段即可,无需硬编码。

内容的提问来源于stack exchange,提问作者android developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 15:10:16