如何使用 ContentProviderOperation 将城市字段添加到 Android 中的联系人

How to add city field to a contact in Android using ContentProviderOperation

我正在创建一个联系人应用程序,它将具有联系人应用程序的所有基本功能(当然还有一些额外的功能)。在实现基本功能时,我卡在了一个地方:

我有一个 activity,用户可以在其中更改联系人的城市名称。如果用户已经有一个城市,我可以使用以下代码更新它:

ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
String contactId = id; // got it from ContactsContract.Contacts._ID
String mimeType = ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE;
String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredPostal.TYPE + " = ?";
String[] values = new String[]{contactId, mimeType, String.valueOf(ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME)};
ops.add(
 android.content.ContentProviderOperation.newUpdate(
  android.provider.ContactsContract.Data.CONTENT_URI)
   .withSelection(selection, values)
   .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, "California")
   .build()
);
contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);

但是,上面的代码不适用于除了 phone 号码之外没有任何详细信息的联系人。浏览了很多之后,我找到了以下方法:

ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
String rawContactId = id;
String mimeType = ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE;
String[] values = new String[]{contactId, mimeType, String.valueOf(ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME)};
ops.add(
 android.content.ContentProviderOperation.newInsert(
  android.provider.ContactsContract.Data.CONTENT_URI)
   .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
   .withValue(ContactsContract.Data.MIMETYPE, mimeType)
   .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, "California")
   .build()
);
contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);

在上述情况下,我从 ContactsContract.RawContacts.CONTENT_URI 获得 rawContactId,其中 ContactsContract.Data.CONTACT_ID 等于正常的 contactId。我为不同的帐户获得了不同的 rawContactId - Google、WhatsApp、Skype 等。我尝试使用所有 rawContactId 进行更新,但它仍然没有得到更新。谁能帮我解决一下?

当你应用上面的代码时,你没有具体说明是什么不起作用,但我可以看到其中的一些问题。

首先你需要清楚的了解联系人数据是如何存储在数据库中的:

  1. Contacts - 每个条目代表一个联系人,并将一个或多个 RawContacts
  2. 组合在一起
  3. RawContacts - 每个条目代表有关联系人的数据,这些数据由某些 SyncAdapter(例如 Whatsapp、Google、Facebook、Viber)同步,这将多个数据条目分组
  4. Data - 有关联系人、电子邮件、电话等的实际数据。每行是属于单个 RawContact
  5. 的单个数据

问题 1

因此,如果您尝试 更新 现有的邮政地址,您应该注意不要使用 contactId 作为您的密钥,因为单个联系人(由 contactId 引用) 可能在多个原始联系人中有多个邮政地址,每个联系人都有多个邮政地址数据行。 然后,您的 newUpdate 调用可能会更新所有地址中的城市。

因此,如果您的联系人 "David" 的地址为:

  • 123 巷,纽约,美国
  • 456 车道,美国密苏里州
  • 法国巴黎789巷

并且您的用户现在正在尝试将 "Paris" 更新为 "Lyon",您的代码可能会将所有 3 个地址更新为里昂。

您的密钥必须是您尝试更新的特定 Data 行的当前 Data._ID

问题 2

如果您尝试将新数据行插入现有原始联系人,例如一个全新的邮政-地址,您需要指定您要插入的特定 RawContact ID,而不是使用 withValueBackReference - 这仅在您现在创建一个全新的 RawContact 并且不知道会发生什么时有用是它将获得的 RawContact ID,因此您正在对新 RawContact 将从先前调用 RawContact 行的 newInsert 获得的 ID 进行反向引用。

此外,在这种情况下,仅 CITY 值是不够的,因为您将获得仅由 CITY 组成的完整邮政地址,如下所示:

  • 123 巷,纽约,美国
  • 456 车道,美国密苏里州
  • 法国巴黎789巷
  • 里昂

您在这里要做的是收集所有邮政地址值,并将它们全部添加到一个新的 Data 行中,如下所示:

ops.add(
 ContentProviderOperation.newInsert(Data.CONTENT_URI)
   .withValue(Data.RAW_CONTACT_ID, rawContactId)
   .withValue(Data.MIMETYPE, mimeType)
   .withValue(StructuredPostal.STREET, "123 Lane")
   .withValue(StructuredPostal.CITY, "Los Angles")
   .withValue(StructuredPostal.REGION, "California")
   .withValue(StructuredPostal.COUNTRY, "United States")
   .build()
);

问题 3

如果您只想将 CITY 值插入 existing 邮政地址行,您需要执行以下操作对特定数据 ID 的更新,而不是插入,如下所示:

String selection = Data._ID + " = ?";
ops.add(
 ContentProviderOperation.newUpdate(Data.CONTENT_URI)
   .withSelection(selection, new String[]{ dataId })
   .withValue(StructuredPostal.CITY, "Los Angeles")
   .build()
);