How to respectfully get your user contact details ?
My app KipinTooch is a kind of contact management app. I created it with the following intentions:
- Have a better view among the common informations from different contacts.
- Highlight the relations between contacts.
- Allow a batch modification for those informations.
- Help filling informations in a contact card based on similar contacts.
I also wanted the starting point to be the user. So, the app need to gather some informations about the user. But I also wanted the app to be self-sufficient. The app should process data from the phone and everything is done on the phone. No third party social network, no sending to a unknown distant server which would do its magic. This is what I call to be respectful: the user private data remains private and on its device.
This mean that if you want to provide the user some similar informations, you can only rely on what’s on the device. In this post, I will show you how you can gather those informations.
The Me contact.
Since Android 4, there is a “Me” contact created by the system. This is supposed to be the owner of the device. Don’t expect to find too much informations there as most users does not see any reason to fill this profile. To be honest, I don’t.
But you can expect to find at least a name, which is pulled from the Google+ account. As I distribute KipinTooch on Google Play, this should be a name associated to the Google account. To get that name, you first need to declare the privilege (most of the manifest content have been ignored.
<!--?xml version="1.0" encoding="utf-8"?-->
Then, query the profile in a similar way as you’ll do for any contact.
String[] projection = new String[] {ContactsContract.Profile.DISPLAY_NAME}; Uri dataUri = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, ContactsContract.Contacts.Data.CONTENT_DIRECTORY); Cursor c = contentResolver.query(dataUri, projection, null, null, null); try { if (c.moveToFirst()) { String displayName = c.getString(c.getColumnIndex(ContactsContract.Profile.DISPLAY_NAME)); } } finally { c.close(); }
Here you got a Display Name. You may also expect to find a profile picture but not really anything else.
The accounts.
As KipinTooch is on the Play Store, I can expect to find a Google account. Google accounts IDs are e-mail addresses. So, first, you have to declare the right privilege:
<!--?xml version="1.0" encoding="utf-8"?-->
Then, get the Google accounts and thus, the associated IDs. Yes, consider that you may have several accounts on a single device.
AccountManager aManager = AccountManager.get(context); Account[] accounts = aManager.getAccountsByType("com.google"); for (Account account : accounts) { String accountId = account.name; }
You can also get e-mail addresses from other accounts such as the native e-mail, Facebook or Dropbox.
At this point, you may have a name and a e-mail address. If you don’t have any, you’ll have to ask the user.
If you have at least one of it, it is time to query the contacts database.
Using the Contacts Provider
Many users store their own informations as a contact. You can query for users with the name you got from the Me contact or the e-mail address from the accounts. Of, course, you’ll first need to ask for the privilege to read contacts information:
<!--?xml version="1.0" encoding="utf-8"?-->
You can now look for a contact which may be the user. This is the basic code for looking for a contact using its DisplayName.
String displayName = getMeDisplayName(); // You should check for null or empty values, not shown here // This projection is the most basic String[] projection = {ContactsContract.Contacts._ID}; Cursor c = contentResolver.query( ContactsContract.Contacts.CONTENT_URI, projection, ContactsContract.Contacts.DISPLAY_NAME + " = ?", new String[] {displayName}, null ); // Then parse the cursor
If you successfully get a contact, you can assume it is your user with all its informations. Of course, if you have a doubt about it, you can still ask the user to confirm.
Relations
Kipintooch is meant to highlight common informations, and especially, relations. You can store some contact relations in its contact card. So, you can of course read those relations:
String[] projection = new String[] { ContactsContract.CommonDataKinds.Relation.TYPE, ContactsContract.CommonDataKinds.Relation.NAME, ContactsContract.CommonDataKinds.Relation.LABEL }; Cursor c = contentResolver.query( ContactsContract.Data.CONTENT_URI, projection, ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.Relation.CONTACT_ID + " = ? ", new String[] {ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE, String.valueOf(user.getId())}, null );
The TYPE will inform you about the kind of relation (Spouse, child, manager…). The LABEL is used for the custom type. The NAME is the name of the relation as the user entered it. You can expect this name to be a Display Name but there is no hint for that. It is up to you to build the best strategy to look for this contact. One good start is to use the CONTENT_FILTER_URI. The proper usage is to build a query with the proper URI.
Cursor c = contentResolver.query( Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, contactName);, projection, null, null, null );
The String contactName will be considered to be a partial name and will be used to match various part of the contact name. Of course, you can consider that you should query the name using this URI rather than using the CONTENT_URI and look for the DisplayName.
Finding a relation without any relation set
Contact cards always contain incomplete informations. Lets say you’ve met Anna, the wife of your friend John, and you add her as a contact in your address book. To remember who she is, you may add John as a relation with the type Spouse. But this does not mean that you’ll then go to John’s card and add Anna as Spouse as you’ll have to look for another card and edit it.
So, don’t forget to look in the other contacts for any relations with the user. The following code is a starting point.
String[] projection = new String[] { ContactsContract.CommonDataKinds.Relation.CONTACT_ID, }; Cursor c = contentResolver.query( ContactsContract.Data.CONTENT_URI, projection, ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.Relation.NAME + " = ? ", new String[] {ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE, user.getDisplayName()}, null );
Of course, this code assumes that you are looking for the Display Name, but you will face the same difficulties as in the previous part.
As you can see, using the user’s contact book, it is easy to build a contact graph, even with incomplete informations. Even if I used the word respectfully in the title, some apps and services do, of course, compute those relations for their database. As I always say, those are personal data and should not be send away from the device without warning the user and his consent.
About Darko Stankovski
Darko Stankovski is the founder and editor of Dad 3.0. You can find more about him trough the following links.