如何获取可保存联系人的合规账户及多SIM卡账户数据?
解决方案:合规联系人存储账户获取与多SIM卡支持
一、获取用户预期的合规联系人存储账户
你之前使用ContentResolver.getSyncAdapterTypes()得到大量无关账户(如Telegram、Duo),原因是这类IM应用的SyncAdapter也注册了ContactsContract.AUTHORITY用于同步自身联系人,但并非用户预期的“联系人存储账户”。以下是无硬编码的通用筛选方案:
核心思路
- 先获取所有可同步联系人的账户(通过
ContactsContract.Settings筛选已启用同步的账户) - 再过滤掉非系统级/非联系人专属的账户:通过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卡账户:
核心思路
- 先从AccountManager中筛选所有账户类型包含
sim关键词的账户(覆盖不同厂商的SIM账户类型) - 结合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
相关产品推荐
相关产品推荐

