Search code examples
androidandroid-contactscontactscontract

Android ContactsContract class: How to ignore non-primary ACCOUNT_TYPES?


So I know by now that I can use ContactsContract class to list all contacts available on an android device. Something like this:

private void getContacts(){

ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(ContactsContract.contacts.CONTENT_URI,null,null,null,null);

while(cursor.moveToNext){

//get contact id
.....
//get contact name
....

}

}

What do I mean by contact above:

A contact according to my understanding is a set of raw_contacts. Example:

These are 2 contacts in the phone book:

[ User A  ]
----------- 
[ User B  ]

Upon clicking User A I will get this:

   | User A                                             |
   | phone 1: 0000 mobile                               | 
   | phone 2: 1111 home                                 |
   | phone 3: 2222 work                                 |
   |                                                    |
   |        linked :google , sim, phone, viber, whatsapp|

From my understanding:

  • Contacts = User A or User B.

  • raw_contacts = User A (phone) or User A (SIM) or User A (google) or User A (viber)....

My question is:

If I looped through all contacts and then looped through all raw_contacts in a contact keeping in mind that raw_contacts can be alot, and then looped through phone number (home, mobile, work...) of each raw contact...Then wouldn't it be bad for performance?

What should I do to only loop through mobile numbers that are stored on phone (sim, or device) without having to loop through raw_contacts that are generated by custom apps?

It makes no sense to loop through all raw_contacts.

Apps like whatsapp or viber or telegram or any phone app get these contacts fast and efficiently.

Thanks.


Solution

  • ...Then wouldn't it be bad for performance?

    Definitely bad performance.

    What should I do to only loop through mobile numbers?

    Iterate directly over the ContactsContract.Data table, to get all phones from all RawContacts.

    Apps like whatsapp or viber or telegram or any phone app get these contacts fast and efficiently.

    This is partially false, it seems like it's super-fast because those app run a service to query for contacts, communicate with the server, and then cache results locally. Then they only need to query periodically for the delta (contacts added/removed). Even with the faster code below, run this in a background thread, as it may take time to run depending on the number of contacts on the device.

    Sample code:

    private class ContactInfo {
        public long id;
        public String name;
        Set<String> phones = new HashSet<>();
    
        public ContactInfo(long id, String name) {
            this.id = id;
            this.name = name;
        }
    }
    
    Map<Long, ContactInfo> contacts = new HashMap<Long, ContactInfo>();
    
    String[] projection = {Data.CONTACT_ID, Data.DISPLAY_NAME, Data.DATA1};
    
    // limit data results to just phone numbers.
    // Note: you can potentially restrict the query to just phone & SIM contacts,
    // but that would actually make the query slower not faster, because you'll need multiple queries over the DB, 
    // instead of just a big one.
    String selection = Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'";
    
    Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
    
    while (cur.moveToNext()) {
        long id = cur.getLong(0);
        String name = cur.getString(1);
        String data = cur.getString(2); // the actual info, e.g. +1-212-555-1234
    
        Log.d(TAG, "got " + id + ", " + name + ", " + data;
    
        // add found phone to existing ContactInfo, or create a new ContactInfo object
        ContactInfo info;
        if (contacts.containsKey(id)) {
            info = contacts.get(id);
        } else {
            info = new ContactInfo(id, name);
            contacts.put(id, info);
        }
        info.phones.add(data);
    }
    cur.close();
    
    // you now have a mapping between contact-id to an info object containing id, name, and a list of phone-numbers!