0

I'm currently using the answer to this question: How to get all contacts first name, last name, email, phone number, etc without duplicates to retrieve the user contact list with, for each contact, all phone numbers, the firstname, lastname and the photo id.

The issue I've with that answer is it's creating one request per data wanted. It's creating performances issues on my app. I would like to retrieve all these information from a single request, but I've got a lot of difficulty to understand how Android's query are managed and where are stored data I'm looking for.

Example of what I've tried (I've done a lot of test without success, following one is the last one I've tried):

    new CursorLoader(
        this,
        ContactsContract.Data.CONTENT_URI,
        null,
        ContactsContract.Data.HAS_PHONE_NUMBER + "!=0 AND (" + ContactsContract.Data.MIMETYPE + "=?)",
        new String[]{ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE},
        ContactsContract.Data.CONTACT_ID)
    ; 

And information I would like to retrieve:

        int firstNameCol = cursor.getColumnIndex(CommonDataKinds.StructuredName.GIVEN_NAME);
        int lastNameCol = cursor.getColumnIndex(CommonDataKinds.StructuredName.FAMILY_NAME);
        int photoIdCol = cursor.getColumnIndex(CommonDataKinds.StructuredName.PHOTO_ID);
        int phoneCol = cursor.getColumnIndex(Phone.NUMBER);

There is two issues with that request:

  • I only get one phone number per contact, however some of my contacts have several numbers and I would like to retrieve them

  • I'm not retrieving the firstname and the lastname (I get a weird number instead)

Please note that I don't want to retrieve the DISPLAY_NAME, I want both given_name and family_name. Please also note I would like to have one result per contact and not one result per phone number (i.e one result contain one to many phone numbers)

Any idea to do that in a single request?

EDIT: trying with another piece of code from @pskink but I get a "column '_id' does not exists" error

    String[] projection = {
        ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
        ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
        ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.StructuredName.PHOTO_ID
    };
    String selection = ContactsContract.Data.MIMETYPE + " in (?, ?, ?, ?)";
    String[] selectionArgs = {
        ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
        ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
        ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.StructuredName.PHOTO_ID
    };
    //String sortOrder = ContactsContract.Contacts.SORT_KEY_ALTERNATIVE;

    //Uri uri = ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
    Uri uri = ContactsContract.Data.CONTENT_URI;
    // ok, let's work...
    return new CursorLoader(
        this, uri, projection, selection, selectionArgs, null
    );
Community
  • 1
  • 1
Sébastien BATEZAT
  • 2,353
  • 26
  • 42
  • see http://stackoverflow.com/a/26820544/2252830 and modify it to match your particular needs – pskink Aug 04 '15 at 17:55
  • That's the thing I don't know how to adapt it to my needs. See my update, I'm trying, but I get a "column '_id' does not exists" error. Moreover, your solution is doing some check to retrieve the list of email in an object (if (mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)), I cannot do that, I really need to retrieve all phone numbers in one row, because i'm using a cursor adapter on my list view, and each item has to be linked to one contact, not one phone number. – Sébastien BATEZAT Aug 04 '15 at 19:22
  • sorry, i have no idea what you are talking about, did you try to add my code in Activity.onCreate ? – pskink Aug 04 '15 at 19:25
  • (sorry - you are too quick, I was updating my answer that why you was not able to understand) No, I didn't try to add your code, as it doesn't fit to my needs. There is a lot of example all over the web, each of them are working, but I'm not able to find something that fit my needs (in one request, as a quick reminder, i've got something working right now but with many requests and it's taking too much time) – Sébastien BATEZAT Aug 04 '15 at 19:28
  • mine uses one request, see the source, and try to run it, that way you will understand how it works, and ... its fast as hell (just uncomment Log.d stuff in `for (AddressBookContact addressBookContact : list) {` loop) – pskink Aug 04 '15 at 19:34
  • other examples you can find all over the web are like Citroen 2CV, mine is like Ferrari, tun it and you will see the difference... – pskink Aug 04 '15 at 19:43
  • Yes you are using one request, but you are retrieving one email and one phone per row, I need to retrieve all phone from a contact in a single row (basically a row = a contact, not a phone or an email). The goal is to show the result in a list view (with a cursor adapter) – Sébastien BATEZAT Aug 04 '15 at 19:48
  • and what if (theortically) user has 10000 phone numbers? so the cursor should have 10000 columns: tel0, tel1...tel9999 ? do you have a contact with multiple phones/emails? then now uncomment Log.d and see in the logcat (or even better debug the code to see what happens in my code when a user has multiple phones/emails), also becouse of unlimited numbers of phones/emails you cannot use a Cursor – pskink Aug 04 '15 at 19:51
  • of course you can do that by extending a `AbstractCursor` but... does it pay off? i dont think so, and the `ArrayAdapter` is in that case better... {normally i use `CursorAdapter` as much as possible even by using artificial `CursorMatrix` object) – pskink Aug 04 '15 at 19:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/85147/discussion-between-sebastien-and-pskink). – Sébastien BATEZAT Aug 04 '15 at 20:01
  • see the link below, notice the `switch` statement and how/when the data is read: http://codeshare.io/h6otZ – pskink Aug 05 '15 at 07:59
  • Perfect! Many thanks. I've removed the email part and replace it by a photo id part, and everithing is working just well, and is very fast. Thank you very much for your help on that! Could you please answer to the question so I'll be able to accept your answer? – Sébastien BATEZAT Aug 06 '15 at 19:43
  • 1
    it is you who made a final version (btw you can post an answer here as well); if you want you can upvote my original work – pskink Aug 06 '15 at 19:55

1 Answers1

1

Thanks to @pskink, I've now a fast single request to retrieve what I want. Unfortunately, I cannot use the CursorAdapter and I've to request the whole DB and store result in an object model, but the result is very fast so it's OK.

Here's the complete code:

public ArrayList<ContactModel> retrieveContactList(){

    ArrayList<ContactModel> list = new ArrayList<>();
    LongSparseArray<ContactModel> array = new LongSparseArray<>();

    Set<String> set = new HashSet<String>();
    set.add(ContactsContract.Data.MIMETYPE);
    set.add(ContactsContract.Data.CONTACT_ID);
    set.add(ContactsContract.Data.PHOTO_ID);
    set.add(ContactsContract.CommonDataKinds.Phone.NUMBER);
    set.add(ContactsContract.CommonDataKinds.Phone.TYPE);
    set.add(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
    set.add(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
    set.add(ContactsContract.Contacts.PHOTO_ID);

    Uri uri = ContactsContract.Data.CONTENT_URI;
    String[] projection = set.toArray(new String[0]);
    String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)";
    String[] selectionArgs = {
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
    };
    String sortOrder = ContactsContract.Contacts.SORT_KEY_ALTERNATIVE;

    Cursor cursor = this.context.getContentResolver().query(
        uri,
        projection,
        selection,
        selectionArgs,
        sortOrder
    );

    final int mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE);
    final int idIdx = cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
    final int phoneIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
    final int phoneTypeIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
    final int givenNameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
    final int familyNameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
    final int photoIdIdx = cursor.getColumnIndex(ContactsContract.Data.PHOTO_ID);

    while (cursor.moveToNext()) {

        long id = cursor.getLong(idIdx);
        ContactModel addressBookContact = array.get(id);

        if (addressBookContact == null) {
            addressBookContact = new ContactModel(id);
            array.put(id, addressBookContact);
            list.add(addressBookContact);
        }

        Long photoId = cursor.getLong(photoIdIdx);
        if (photoId != null){
            addressBookContact.addPhotoId(photoId);
        }

        switch (cursor.getString(mimeTypeIdx)) {
            case ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE:
                // row's data: see ContactsContract.CommonDataKinds.Phone
                addressBookContact.addPhone(cursor.getInt(phoneTypeIdx), cursor.getString(phoneIdx));
                break;
            case ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE:
                // row's data: see ContactsContract.CommonDataKinds.StructuredName
                addressBookContact.addName(cursor.getString(givenNameIdx), cursor.getString(familyNameIdx));
                break;
        }
    }

    cursor.close();

    return list;
}
Sébastien BATEZAT
  • 2,353
  • 26
  • 42
  • 1
    btw: do you need `ContactsContract.Contacts.CONTENT_ITEM_TYPE` in `selectionArgs` (and three "?" in `selection`) ? you don't use it anywhere... – pskink Aug 07 '15 at 04:59
  • Yeah, you right it's useless, I've tried with CONTENT_ITEM_TYPE first time for photo id, and I forgot later to remove it. Answer edited. Thanks again! – Sébastien BATEZAT Aug 07 '15 at 06:51
  • i found the solution using SimpleCursorAdapter ;-), please compare the speed with this: http://codeshare.io/eOxal – pskink Aug 10 '15 at 09:40