Search code examples
androidandroid-contacts

How to get Contact ID, Email, Phone number in one SQLite query ? Contacts Android Optimization


I want to fetch All Contacts atleast with one phone Number, also I want all Phone Numbers and All emails for every Contact.

Current code :

// To get All Contacts having atleast one phone number.

Uri uri = ContactsContract.Contacts.CONTENT_URI;
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " > ?";
String[] selectionArgs = new String[] {"0"};
Cursor cu = applicationContext.getContentResolver().query(uri, 
                null, selection, selectionArgs, null);

// For getting All Phone Numbers and Emails further queries : 
while(cu.moveToNext()){
String id = cu.getString(cu.getColumnIndex(ContactsContract.Contacts._ID));


 // To get Phone Numbers of Contact
    Cursor pCur = context.getContentResolver().query(
    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,  null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
 new String[]{id}, null);

// To get Email ids of Contact
Cursor emailCur = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?",
new String[]{id}, null); 

// Iterate through these cursors to get Phone numbers and Emails
}

If there are more than 1000 contacts in my Device, it is taking too much time. How can I get All Data in single query, rather than doing two additional queries for each contact?

Or is there any other way to optimize?

Thank you in Advance.


Solution

  • ICS: When you query from Data.CONTENT_URI you have all the rows from the associated Contact already joined - i.e. this would work:

    Java:

        ContentResolver resolver = getContentResolver();
        Cursor c = resolver.query(
                Data.CONTENT_URI, 
                null, 
                Data.HAS_PHONE_NUMBER + "!=0 AND (" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?)", 
                new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
                Data.CONTACT_ID);
        
        while (c.moveToNext()) {
            long id = c.getLong(c.getColumnIndex(Data.CONTACT_ID));
            String name = c.getString(c.getColumnIndex(Data.DISPLAY_NAME));
            String data1 = c.getString(c.getColumnIndex(Data.DATA1));
            
            System.out.println(id + ", name=" + name + ", data1=" + data1);
        }
    

    Kotlin:

            var c : Cursor? = null
            try {
                    val resolver : ContentResolver = this.contentResolver //Replace "this" With Whatever The Reference To Your Activity Is (If Need Be)
                    c = resolver.query(
                        ContactsContract.Data.CONTENT_URI,
                        null,
                        ContactsContract.Data.HAS_PHONE_NUMBER + "!=0 AND (" + ContactsContract.Data.MIMETYPE + "=? OR " + ContactsContract.Data.MIMETYPE + "=?)",
                        arrayOf(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE),
                        ContactsContract.Data.CONTACT_ID)
    
                    if (c!!.count > 0) {
                        while (c.moveToNext()) {
                            val id : Long = c.getLong(c.getColumnIndex(ContactsContract.Data.CONTACT_ID))
                            val name : String = c.getString(c.getColumnIndex(ContactsContract.Data.DISPLAY_NAME))
                            val data1 : String = c.getString(c.getColumnIndex(ContactsContract.Data.DATA1))
    
                            Log.d("Tag", "$id) name=$name, data1=$data1")
                        }
                    }
            } finally {
                c?.close() //<-- close if not null
            }
    

    If you are targeting 2.3 you need to account for the fact that HAS_PHONE_NUMBER is not available through the joins used when querying Data.

    Fun.

    This could, for instance, be solved either by skipping your requirement that the contact must have a phone number and instead settle for "any contact with at least a phone number or an e-mail address":

    Cursor c = resolver.query(
            Data.CONTENT_URI, 
            null, 
            Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?", 
            new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
            Data.CONTACT_ID);
    

    If that is not an option you can always go for a horribly hacky sub-select:

    Cursor c = resolver.query(
            Data.CONTENT_URI, 
            null, 
            "(" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?) AND " + 
            Data.CONTACT_ID + " IN (SELECT " + Contacts._ID + " FROM contacts WHERE " + Contacts.HAS_PHONE_NUMBER + "!=0)", 
            new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE}, Data.CONTACT_ID);
    

    or solve it by using two Cursors:

    Cursor contacts = resolver.query(Contacts.CONTENT_URI, 
            null, Contacts.HAS_PHONE_NUMBER + " != 0", null, Contacts._ID + " ASC");
    Cursor data = resolver.query(Data.CONTENT_URI, null, 
            Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?", 
            new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE}, 
            Data.CONTACT_ID + " ASC");
    
    int idIndex = contacts.getColumnIndexOrThrow(Contacts._ID);
    int nameIndex = contacts.getColumnIndexOrThrow(Contacts.DISPLAY_NAME);
    int cidIndex = data.getColumnIndexOrThrow(Data.CONTACT_ID);
    int data1Index = data.getColumnIndexOrThrow(Data.DATA1);
    boolean hasData = data.moveToNext();
    
    while (contacts.moveToNext()) {
        long id = contacts.getLong(idIndex);
        System.out.println("Contact(" + id + "): " + contacts.getString(nameIndex));
        if (hasData) {
            long cid = data.getLong(cidIndex);
            while (cid <= id && hasData) {
                if (cid == id) {
                    System.out.println("\t(" + cid + "/" + id + ").data1:" + 
                            data.getString(data1Index));
                }
                hasData = data.moveToNext();
                if (hasData) {
                    cid = data.getLong(cidIndex);
                }
            }
        }
    }