I have managed to extract contact details from the phone by using ContactContract example I found, but I noticed that most of the people on my phone has a unique id key associated to their emails and phone numbers separately. For example, Alan's contact detail is split up as following when I extract it out from database even though they are for the same person:
key name email phone
20121 Alan alan@gmail.com null
20133 Alan null 04xxxxxxxx
So how does the phone manage the association with all these different keys in the contact (I assume there must be a separate table for it)? Is there any way to obtain this association? Because I can not just try match the name as people can have exactly the same name, you have to keep them separated as how they are stored on your phone contact.
(Or the messed up situation is due to all apps are able to save contact related details into the same database on the phone?)
My code looks like following (I forgot where I get this code from, but getDetailedContactList function is returning a list of contact of the above problem):
public static String CONTACT_ID_URI = ContactsContract.Contacts._ID;
public static String DATA_CONTACT_ID_URI = ContactsContract.Data.CONTACT_ID;
public static String MIMETYPE_URI = ContactsContract.Data.MIMETYPE;
public static String EMAIL_URI = ContactsContract.CommonDataKinds.Email.DATA;
public static String PHONE_URI = ContactsContract.CommonDataKinds.Phone.DATA;
public static String NAME_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Data.DISPLAY_NAME_PRIMARY : ContactsContract.Data.DISPLAY_NAME;
public static String PICTURE_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Contacts.PHOTO_THUMBNAIL_URI : ContactsContract.Contacts.PHOTO_ID;
public static String MAIL_TYPE = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
public static String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
public Cursor getContactCursor(String stringQuery, String sortOrder) {
Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Log.e(TAG, "ContactCursor search has started...");
Long t0 = System.currentTimeMillis();
Uri CONTENT_URI;
if (stringQuery == null)
CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
else
CONTENT_URI = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode(stringQuery));
String[] PROJECTION = new String[]{
CONTACT_ID_URI,
NAME_URI,
PICTURE_URI
};
String SELECTION = NAME_URI + " NOT LIKE ?";
String[] SELECTION_ARGS = new String[]{"%" + "@" + "%"};
Cursor cursor = getContentResolver().query(CONTENT_URI, PROJECTION, SELECTION, SELECTION_ARGS, sortOrder);
Long t1 = System.currentTimeMillis();
Log.e(TAG, "ContactCursor finished in " + (t1 - t0) / 1000 + " secs");
Log.e(TAG, "ContactCursor found " + cursor.getCount() + " contacts");
Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
public Cursor getContactDetailsCursor() {
Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Log.e(TAG, "ContactDetailsCursor search has started...");
Long t0 = System.currentTimeMillis();
String[] PROJECTION = new String[]{
DATA_CONTACT_ID_URI,
MIMETYPE_URI,
EMAIL_URI,
PHONE_URI
};
String SELECTION = NAME_URI + " NOT LIKE ?" + " AND " + "(" + MIMETYPE_URI + "=? " + " OR " + MIMETYPE_URI + "=? " + ")";
String[] SELECTION_ARGS = new String[]{"%" + "@" + "%", ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
Cursor cursor = getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
PROJECTION,
SELECTION,
SELECTION_ARGS,
null);
Long t1 = System.currentTimeMillis();
Log.e(TAG, "ContactDetailsCursor finished in " + (t1 - t0) / 1000 + " secs");
Log.e(TAG, "ContactDetailsCursor found " + cursor.getCount() + " contacts");
Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
public List<ContactViewModel> getDetailedContactList(String queryString) {
/**
* First we fetch the contacts name and picture uri in alphabetical order for
* display purpose and store these data in HashMap.
*/
Cursor contactCursor = getContactCursor(queryString, NAME_URI);
if(contactCursor.getCount() == 0){
contactCursor.close();
return new ArrayList<>();
}
List<Integer> contactIds = new ArrayList<>();
if (contactCursor.moveToFirst()) {
do {
contactIds.add(contactCursor.getInt(contactCursor.getColumnIndex(CONTACT_ID_URI)));
} while (contactCursor.moveToNext());
}
HashMap<Integer, String> nameMap = new HashMap<>();
HashMap<Integer, String> pictureMap = new HashMap<>();
int idIdx = contactCursor.getColumnIndex(CONTACT_ID_URI);
int nameIdx = contactCursor.getColumnIndex(NAME_URI);
int pictureIdx = contactCursor.getColumnIndex(PICTURE_URI);
if (contactCursor.moveToFirst()) {
do {
nameMap.put(contactCursor.getInt(idIdx), contactCursor.getString(nameIdx));
pictureMap.put(contactCursor.getInt(idIdx), contactCursor.getString(pictureIdx));
} while (contactCursor.moveToNext());
}
/**
* Then we get the remaining contact information. Here email and phone
*/
Cursor detailsCursor = getContactDetailsCursor();
HashMap<Integer, String> emailMap = new HashMap<>();
HashMap<Integer, String> phoneMap = new HashMap<>();
idIdx = detailsCursor.getColumnIndex(DATA_CONTACT_ID_URI);
int mimeIdx = detailsCursor.getColumnIndex(MIMETYPE_URI);
int mailIdx = detailsCursor.getColumnIndex(EMAIL_URI);
int phoneIdx = detailsCursor.getColumnIndex(PHONE_URI);
String mailString;
String phoneString;
if (detailsCursor.moveToFirst()) {
do {
/**
* We forget all details which are not correlated with the contact list
*/
if (!contactIds.contains(detailsCursor.getInt(idIdx))) {
continue;
}
if(detailsCursor.getString(mimeIdx).equals(MAIL_TYPE)){
mailString = detailsCursor.getString(mailIdx);
/**
* We remove all double contact having the same email address
*/
if(!emailMap.containsValue(mailString.toLowerCase()))
emailMap.put(detailsCursor.getInt(idIdx), mailString.toLowerCase());
} else {
phoneString = detailsCursor.getString(phoneIdx);
phoneMap.put(detailsCursor.getInt(idIdx), phoneString);
}
} while (detailsCursor.moveToNext());
}
contactCursor.close();
detailsCursor.close();
/**
* Finally the contact list is build up
*/
List<ContactViewModel> contacts = new ArrayList<>();
Set<Integer> emailsKeySet = emailMap.keySet();
Set<Integer> phoneKeySet = phoneMap.keySet();
for (Integer key : contactIds) {
if( (!emailsKeySet.contains(key) && !phoneKeySet.contains(key))
|| (emailMap.get(key) == null && phoneMap.get(key) == null)
|| mContactDB.isContactExisted(key))
{
continue;
}
contacts.add(new ContactViewModel(key, nameMap.get(key), emailMap.get(key)));
}
return contacts;
}