1

I want to display contacts, which has phone numbers, but with all assigned numbers. I want to improve the performance. Is there any more effective way to do this? Like obtaining all contacts thumbnails at once? This approach somehow fails for me, as the cursor is not empty, but returns empty(?) uri.

I have done some time tracking and it looks like appendContactNumber() takes to execute from 15 ms (one phone number) up to about 20 ms (three phone numbers).

// List specific variables
private static ArrayList<String> Contacts;
private static ArrayList<String> Numbers;
private static ArrayList<Bitmap> Photo;

// ContentResolver query specific variables
private static final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;
private static final String[] CONTACTS_PROJECTION = {
        ContactsContract.Contacts._ID,
};
private static final String CONTACTS_SELECTION = ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1";
private static final String CONTACTS_SORT_ORDER = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " COLLATE LOCALIZED ASC";

private static final Uri PHONES_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
private static final String[] PHONE_PROJECTION = {
        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Phone.NUMBER
};
private static final String PHONE_SELECTION = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ";
private static final String[] PHOTO_PROJECTION = { ContactsContract.Contacts.Photo.PHOTO };

private Context context;

public ContactsLoader(Context context) {
    this.context = context;
}

@Override
public void run()
{
    loadContacts();
}

private void loadContacts() {
    Contacts = new ArrayList<>();
    Numbers = new ArrayList<>();
    Photo = new ArrayList<>();

    // Retrieve all contacts with phone numbers
    Cursor contactsCursor = context.getContentResolver().query(
            CONTACTS_URI,
            CONTACTS_PROJECTION,
            CONTACTS_SELECTION,
            null,
            CONTACTS_SORT_ORDER
    );

    if (contactsCursor != null) {
        while (contactsCursor.moveToNext()) {
            appendContactNumber(contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.Contacts._ID)));
        }
        contactsCursor.close();
    }
}

private void appendContactNumber(final String contactId) {
    // Retrieve phone numbers for contact specified by id
    Cursor numbersCursor = context.getContentResolver().query(
            PHONES_URI,
            PHONE_PROJECTION,
            PHONE_SELECTION + contactId,
            null,
            null
    );

    // If phone numbers cursor is not empty
    if (numbersCursor != null) {
        Bitmap thumbnail = getContactThumb(contactId);
        while (numbersCursor.moveToNext()) {
            Contacts.add(numbersCursor.getString(numbersCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
            Numbers.add(numbersCursor.getString(numbersCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
            Photo.add(thumbnail);
        }
        numbersCursor.close();
    }
}

private Bitmap getContactThumb(final String contactId) {
    // Get contact thumbnail for given contactId
    Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(contactId));
    Uri photoUri = Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
    Cursor thumbnailCursor = context.getContentResolver().query(
            photoUri,
            PHOTO_PROJECTION,
            null,
            null,
            null
    );

    if (thumbnailCursor != null) {
        // If contact thumbnail is not empty
        if (thumbnailCursor.moveToFirst()) {
            Bitmap contactPhoto = BitmapFactory.decodeStream(new ByteArrayInputStream(thumbnailCursor.getBlob(0)));
            thumbnailCursor.close();
            return contactPhoto;
        }
    }
    // Default Bitmap
    return BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_contact_picture);
}
Community
  • 1
  • 1
XXXOOO
  • 51
  • 1
  • 6
  • Some additional time tracking results: Now it takes 2,5 seconds for query 100 contacts. Using default image from resources: 700 ms. Running image fetching in separate thread: about 1 second. – XXXOOO Mar 03 '16 at 06:24

1 Answers1

0

I did some workaround: rather than query for Bitmap it is faster to get photo URI. Here is my code:

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.util.Log;

import java.util.ArrayList;

public class PhoneContactsLoader extends ContactsLoader {

    // Class specific variables
    private ArrayList<String> Numbers;

    public PhoneContactsLoader(Context context) {
        super(context);

        // ContentResolver query specific variables
        URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI.toString();
        PROJECTION = new String[] {
                ContactsContract.Contacts._ID,
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
                ContactsContract.CommonDataKinds.Phone.NUMBER,
                ContactsContract.Contacts.PHOTO_URI
        };
        SELECTION = ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1";
        SORT_ORDER = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
    }

    @Override
    protected void fetchContacts() {
        Contacts = new ArrayList<>();
        Numbers = new ArrayList<>();
        Photos = new ArrayList<>();

        Long timer = System.currentTimeMillis();

        // Retrieve all contacts containing phone numbers
        Cursor contactsCursor = context.getContentResolver().query(
                Uri.parse(URI),
                PROJECTION,
                SELECTION,
                null,
                SORT_ORDER
        );

        if (contactsCursor != null) {
            while (contactsCursor.moveToNext()) {
                Contacts.add(contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)));
                Numbers.add(contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
                String photoUri = contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI));
                if (photoUri != null)
                    Photos.add(photoUri);
                else
                    Photos.add("useDefault");
            }
            contactsCursor.close();
        }
        Log.i("PhoneContactsLoader", "Thread execution time: " + (System.currentTimeMillis() - timer) + " ms");
    }

    @Override
    public ArrayList<String> getNumbers() {
        return Numbers;
    }
}
XXXOOO
  • 51
  • 1
  • 6