In part 1 of the article on Android contacts, I’ve presented an easy way of adding a new contact.
The only issue is that after the contact is added, as a return we only get an Uri value, and this may or may not be useful to identify the location of our new contact. It will work when the new contact is unique, and the returned value will be the contact ID, but if the contact gets aggregated, this ID will be useless, since the data we add becomes part of a new contact ID.
try
{
ContentProviderResult[] res = getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
if (res!=null && res[0]!=null) {
newContactUri = res[0].uri;
//02-20 22:21:09 URI added contact:content://com.android.contacts/raw_contacts/612
Log.d(LOG_TAG, "URI added contact:"+ newContactUri);
}
else Log.e(LOG_TAG, "Contact not added.");
}
You can see that reading the res[0].uri indicates a value like :
content://com.android.contacts/raw_contacts/612
Shortly after this code runs, the contact might be combined to one corresponding to the same person, and this ID will point to nothing, since a new aggregate contact is created. More here.
So how to correctly identify the location of the contact we’ve added?
We could search using the contact Name or other fields, but since we can have multiple contacts sharing the same data, this approach is not guaranteed to return the correct result.
A solution that I’ve implemented successfully is creating a custom column in the newly added contact (such as those involved in storing the firstname, phone number, etc), and use it to hold a randomly generated token. After the contact is added and maybe aggregated, searching for this unique token will give us our new contact and all the other details we need.
First we define a custom MIME TYPE for this new sql column:
public static final String MIMETYPE_RADUTOKEN = "vnd.android.cursor.item/radutoken";
Then we generate a random id or token that can be any identifier : number, string, etc:
String szToken = String.format("RADU_TOKEN_%d", System.currentTimeMillis());
For this demo I’ve used a string with a fixed part and a unique suffix given by the currentTimeMillis API.
We add this string when creating the contact, as a new column, among with the firstname, lastname, phone, etc using the ContentProviderOperation:
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(ContactsContract.Data.MIMETYPE, MIMETYPE_RADUTOKEN)
.withValue(ContactsContract.Data.DATA1, szToken)
.build());
The colomn we choose is DATA1 (but can be any other).
Next the contact is created , as presented before. Do identify the contact and read its Lookupkey (that we can use later as a direct pointer), we do a simple search:
boolean foundToken = false;
// IDENTIFY Contact based on name and token
String szLookupKey = "";
Uri lkup = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, szFullname);
ContentResolver cr = getContentResolver();
Cursor idCursor = getContentResolver().query(lkup, null, null, null, null);
// get all the names
while (idCursor.moveToNext()) {
// get current row/contact ID = ID's are unreliable, so we will go for the lookup key
String szId = idCursor.getString(idCursor.getColumnIndex(ContactsContract.Contacts._ID));
String szName = idCursor.getString(idCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
szLookupKey = idCursor.getString(idCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
// for this contact ID, search the custom field
Log.d(LOG_TAG, "Searching token:"+szId+" Name:"+szName +" LookupKey:" + szLookupKey);
//Log.d(LOG_TAG, "search: "+lid + " key: "+key + " name: "+name);
String tokenWhere = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String[] tokenWhereParams = new String[]{szId, MIMETYPE_RADUTOKEN};
Cursor tokenCur = cr.query(ContactsContract.Data.CONTENT_URI, null, tokenWhere, tokenWhereParams, null);
while (tokenCur.moveToNext()) {
String token = tokenCur.getString(tokenCur.getColumnIndex(ContactsContract.Data.DATA1));
if (szToken.compareTo(token) == 0) {
Log.e(LOG_TAG, "Target Token found: " + token);
tokenCur.close();
foundToken = true;
break;
} else Log.d(LOG_TAG, "Another Token found: " + token);
}
tokenCur.close();
if (foundToken) break;
}
idCursor.close();
if (foundToken) {
Log.d(LOG_TAG, "Using szLookupKey:"+ szLookupKey);
}
else {
Log.d(LOG_TAG, "szLookupKey for token:"+ szToken + " Not found!");
}
Not only the custom column is a safe mechanism of getting a direct reference to the new contact we’ve added, but it can also be used to store various fields not available by default.
Complete project source code here:
AndroidContacts-2
This article has 3 Comments