使用 HTC 10 上的 contentResolver 检索 Android 联系人 returns 无结果

Retrieving Android contact using contentResolver on HTC 10 returns no result

我的应用程序在 Android 通讯录中创建了一个联系人,其中包含一个特殊的 SYNC4 密钥,因此我们可以查找我们的特殊联系人(与其他 Android 联系人分开)。但是,我们无法在 HTC 10 设备上读取联系人。我读过 HTC 设备和其他 Android 设备在处理联系人方面的一些差异,但不幸的是没有找到很好的解释或解决这个问题的方法。

联系人的创建过程如下:

private static final String SPECIAL_CONTACT_KEY = "_MY_SPECIAL_CONTACT_KEY_";

// Create an operation with the special SYN4 key
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
        builder.withValue(RawContacts.ACCOUNT_TYPE, null);
        builder.withValue(RawContacts.ACCOUNT_NAME, null);
        builder.withValue(RawContacts.SYNC4, SPECIAL_CONTACT_KEY);
        ops.add(builder.build());

// Add a 'given name' to the list of operations
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
        builder.withValueBackReference(Data.RAW_CONTACT_ID, 0);
        builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
        builder.withValue(StructuredName.GIVEN_NAME, "givenName");
        ops.add(builder.build());

// Add a 'phone number with type work' to the list of operations
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
        builder.withValueBackReference(Data.RAW_CONTACT_ID, 0);
        builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
        builder.withValue(Phone.TYPE, Phone.TYPE_WORK);
        builder.withValue(Phone.NUMBER, "phoneNumber");
        ops.add(builder.build());

// Add a 'note' to the list of operations (to be able to visually discern the contact from other contacts)
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
        builder.withValueBackReference(Data.RAW_CONTACT_ID, 0);
        builder.withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
        builder.withValue(Note.NOTE, "note");
        ops.add(builder.build());

ContentProviderResult[] result = App.getContext().getContentResolver().applyBatch(AUTHORITY, ops);

可以像这样检索联系人:

// Retrieve our own contacts using the special SYNC4 key
Uri uri = Phone.CONTENT_URI;
String[] projection = {Phone.NUMBER};
String mySpecialSelection = RawContacts.SYNC4 + "='" + SPECIAL_CONTACT_KEY + "'";
Cursor cursor = App.getContext().getContentResolver().query(uri, projection, mySpecialSelection, null, null);

光标在所有设备上都填充了正确的联系人数据,但在 HTC 上光标为空。

您不能t/shouldn创建空联系人 ACCOUNT_NAMEACCOUNT_TYPE,这些被称为僵尸联系人,并且不能保证它们会一直存在设备(不仅在 HTC 设备上)。

如果您必须创建新的联系人,您需要为您的应用创建一个自定义帐户,并在该帐户下创建新的contacts/raw-contacts。

参见 this and this。 顺便说一句,您不必拥有 SyncAdapter,但它可以让更新联系人数据等操作变得更容易,并且可以很好地与 Android 生态系统配合使用。

然后您可以通过在查询中查询 ACCOUNT_TYPE = <your custom type> 来找到您自己的 RawContacts

基于,我简化了链接中描述的帐户创建。我不需要我的用户能够创建帐户/登录等,但我自己创建一个帐户,包括。一个联系人,这就是我所需要的。 这是我的代码的简化版本:

我做了一个 MyAppAccountManager class 这样的:

public class MyAppAccountManager
{   
    public boolean createMyAppAccountIfNotExists(Context context)
    {
        return doesMyAppAccountExist(context) || createNewMyAppAccount(context);
    }

    public boolean createNewMyAppAccount(Context context)
    {
        Account account = new Account(context.getString(R.string.account_name), context.getString(R.string.account_type));
        boolean accountCreated = AccountManager.get(context).addAccountExplicitly(account, context.getString(R.string.account_password), null);
        if (accountCreated)
        {
            Log.d("TAG", "Account created");
        }
        else
        {
            Log.d("TAG", "Account creation failed.");
        }
        return accountCreated;
    }

    public boolean doesMyAppAccountExist(Context context)
    {
        return AccountManager.get(context).getAccountsByType(context.getString(R.string.account_type)).length > 0;
    }
}

当我创建联系人时,我不再使用 SYNC4 SPECIAL_CONTACT_KEY,但是帐户(类型和名称):

if (!new MyAppAccountManager().createMyAppAccountIfNotExists(context))
{
    throw new UnableToCreateContact("Could not find or create MyApp account");
}

ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
        builder.withValue(RawContacts.ACCOUNT_TYPE, context.getString(R.string.account_type));
        builder.withValue(RawContacts.ACCOUNT_NAME, context.getString(R.string.account_name));
        ops.add(builder.build());

此外,我需要在清单中描述我的帐户验证器:

    <service
        android:name=".service.MyAppAuthenticatorService"
        android:exported="false">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator"/>
        </intent-filter>
        <meta-data
            android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator"/>
    </service>

指向我的身份验证器服务:

public class MyAppAuthenticatorService
        extends Service
{
    @Override
    public IBinder onBind(Intent intent)
    {
        MyAppAuthenticator authenticator = new MyAppAuthenticator(this);
        return authenticator.getIBinder();
    }
}

需要默认的 MyAppAuthenticator:

public class MyAppAuthenticator
        extends AbstractAccountAuthenticator
{
    public MyAppAuthenticator(Context context)
    {
        super(context);
    }

    @Override
    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType)
    {
        throw new UnsupportedOperationException();
    }

    ... etc

清单还指向authenticator.xml:

<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
                   android:accountType="@string/account_type"
                   android:icon="@drawable/ic_launcher"
                   android:label="@string/account_name"
                   android:smallIcon="@drawable/ic_launcher"/>

最后 - 只是为了使图片完整 - 这是字符串常量:

<string name="account_type">my.app.applicationid.account</string>
<string name="account_name">@string/app_title</string>
<string name="account_password">password</string>