0

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;
}
John Yang
  • 547
  • 1
  • 8
  • 21

2 Answers2

0

Try below code to fetch contact number of specific person.

ContentResolver cr = getContentResolver(); 
Cursor cursor = cr.query(Phone.CONTENT_URI, null, Phone.DISPLAY_NAME + "=?", new String[]{contactName}, null);
if(cursor.getCount() > 0){
cursor.moveToFirst();
do {
   String number = cursor.getString(mCursor.getColumnIndex(Phone.NUMBER));
}while (cursor.moveToNext() ); 

}

Rakesh
  • 756
  • 1
  • 9
  • 19
  • My problem is that the same person has a unique key for email and phone separately. I would like to know how android is able to link these two separate entry of the same person together. – John Yang Feb 08 '16 at 06:57
  • Do not merge two contacts email and phone number although it may have same name. Let user choose to merge it from native contact. Once user will merge email and number of Allan. Your code is perfect to have single object and always linked with phone's native contact. It is recommended to follow contact id unless you have any specific rigid reason to do so. This is suggestion not the counter. In your example itself, you can think of two different persons in two separate contacts with same name as "Allan". Now if you will merge email and phone number, will it be correct or wrong? – Rakesh Feb 08 '16 at 08:06
  • Not sure if we understand each other. So I will rephrase. In my android contact, Alan has the email and phone linked and displayed correctly in the phonebook. When I write the above code to pull the information out, I can only see it as two separate entry with independent key. So how do I get information about the linkage between the email and phone to this person (as the user already choose to merge this information in the contact book). – John Yang Feb 08 '16 at 10:21
  • Try to get details from this link. A reference to a similar problem http://stackoverflow.com/questions/11860475/how-to-get-contact-id-email-phone-number-in-one-sqlite-query-contacts-androi – Rakesh Feb 08 '16 at 11:14
0

Android recommends using content resolvers and content providers to provide nicely packaged data between applications. You should probably not go messing around with the database itself, and it was clearly not designed with that in mind (as your experience demonstrates).

Instead, you should use the content resolver to query the Android ContactsContract to find what you need. There is a class called ContactsContract.Contacts that sounds like the entry point for what you need. Each record returned by a query to the class represents a single contact.

See the Content Providers Developer Guide for further details.

rothloup
  • 1,220
  • 11
  • 29
  • Actually ContactsContract is exactly what I am using. Maybe I will rephrases the question a bit. – John Yang Feb 08 '16 at 06:51
  • I added the code as well, so maybe you can see if any change I can make to solve the issue. – John Yang Feb 08 '16 at 06:58
  • 1
    ok. in that case, are you certain that the contacts (in your example, "Alan") are really the same contact on Android? On my phone, there is an option to link to contacts together because they refer to the same person, but they can also be "unlinked", indicating that Android still retains the original contact cards, and just creates a link between the two. Perhaps that is what you are seeing. – rothloup Feb 08 '16 at 07:18
  • Yes they are linked. When I get the contact from phone, some names have both email and phone associated together fine, but some are not. So i assume it depends on how the information was added to the contact list originally? (you can have email from gmail, or other email source, and you have have phone from sim or you entered it?) I felt like android has another place keeping the linkage between info about the same person. – John Yang Feb 08 '16 at 10:17