I'm creating a contacts app that will have all the basic features of a Contacts app (and some extra features of course). While implementing the basic features, I'm stuck at a place:
I'm having an activity in which the user can change the city name of a contact. If the user is already having a city, I can update it using the following code:
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);
But, the above code is not working for a contact that doesn't have any details other than phone number. After browsing a lot, I've found the following way to do it:
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);
In the above case, I'm getting the rawContactId
from ContactsContract.RawContacts.CONTENT_URI
with ContactsContract.Data.CONTACT_ID
equal to normal contactId. I was getting different rawContactId for different accounts - Google, WhatsApp, Skype, etc. I tried updating with all the rawContactIds, but still it was not getting updated. Can anybody please help me how to fix it?
You didn't specify what exactly is not working when you apply to above code, but I can see a few issues in it.
First you need to have a clear understanding of how contact data is stored in the database:
Contacts
- each entry represents one contact, and groups together one or more RawContacts
RawContacts
- each entry represents data about a contact that was synced in by some SyncAdapter
(e.g. Whatsapp, Google, Facebook, Viber), this groups multiple Data entriesData
- The actual data about a contact, emails, phones, etc. each line is a single piece of data that belongs to a single RawContact
ISSUE 1
So if you're trying to update an existing postal-address, you should be careful not to use contactId as your key, because a single contact (referenced by a contactId) may have multiple postal-addresses in multiple raw-contacts each with multiple postal-address data rows.
your newUpdate
call might then update the city in ALL addresses.
So if you have a contact "David" that has addresses:
and your user is now trying to update "Paris" to "Lyon", your code might update ALL 3 addresses to Lyon.
Your key then must be the current Data._ID
of the specific Data
row you're trying to update.
ISSUE 2
If you're trying to insert a new data row to an existing raw-contact, for example a completely new postal-address, you'll need to specify the specific RawContact ID you're trying to insert into, and not use withValueBackReference
- that's only useful when you're now creating a whole new RawContact, and don't know what will be the RawContact ID it'll get yet, so you're doing a back-reference to the ID your new RawContact will get from a previous call to newInsert of a RawContact row.
Also, in this case just the CITY value is not enough, as you'll get a whole postal-address comprised of CITY only, like this:
What you want to do here is collect all the postal-address values, and add them all into a single new Data row, like so:
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()
);
ISSUE 3
If you're trying to insert just the CITY value to an existing postal-address row, you'll need to do an update, not an insert, into the specific Data ID, something like this:
String selection = Data._ID + " = ?";
ops.add(
ContentProviderOperation.newUpdate(Data.CONTENT_URI)
.withSelection(selection, new String[]{ dataId })
.withValue(StructuredPostal.CITY, "Los Angeles")
.build()
);