contacts are not loading into my list view android

75 Views Asked by At

I am trying to load the contacts in my phone into list view of an android app, but it doesn't loading my contacts. I got a dailog box to allow or deny permissions, when i pressed allow, its showing me a blank screen

I used contact fetcher class to retrieve the contacts. when i DENY permissions, its showing toast as expected but not showing contacts as list view

public class ContactFetcher {

private final Context context;

public ContactFetcher(Context c) {
    this.context = c;
}

public ArrayList<Contact> fetchAll() {
    String[] projectionFields = new String[]{
            ContactsContract.Contacts._ID,
            ContactsContract.Contacts.DISPLAY_NAME,
    };
    ArrayList<Contact> listContacts = new ArrayList<>();
    CursorLoader cursorLoader = new CursorLoader(context,
            ContactsContract.Contacts.CONTENT_URI,
            projectionFields, // the columns to retrieve
            null, // the selection criteria (none)
            null, // the selection args (none)
            null // the sort order (default)
    );

    Cursor c = cursorLoader.loadInBackground();

    final Map<String, Contact> contactsMap = new HashMap<>(c.getCount());

    if (c.moveToFirst()) {

        int idIndex = c.getColumnIndex(ContactsContract.Contacts._ID);
        int nameIndex = 
          c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);

        do {
            String contactId = c.getString(idIndex);
            String contactDisplayName = c.getString(nameIndex);
            Contact contact = new Contact(contactId, contactDisplayName);
            contactsMap.put(contactId, contact);
            listContacts.add(contact);
        } while (c.moveToNext());
    }

    c.close();

    matchContactNumbers(contactsMap);

    return listContacts;
}

public void matchContactNumbers(Map<String, Contact> contactsMap) {
    // Get numbers
    final String[] numberProjection = new String[]{
            Phone.NUMBER,
            Phone.TYPE,
            Phone.CONTACT_ID,
    };

    Cursor phone = new CursorLoader(context,
            Phone.CONTENT_URI,
            numberProjection,
            null,
            null,
            null).loadInBackground();

    if (phone.moveToFirst()) {
        final int contactNumberColumnIndex = 
           phone.getColumnIndex(Phone.NUMBER);
        final int contactTypeColumnIndex = 
             phone.getColumnIndex(Phone.TYPE);
        final int contactIdColumnIndex = phone.getColumnIndex(Phone.CONTACT_ID);

        while (!phone.isAfterLast()) {
            final String number = phone.getString(contactNumberColumnIndex);
            final String contactId = phone.getString(contactIdColumnIndex);
            Contact contact = contactsMap.get(contactId);
            if (contact == null) {
                continue;
            }
            final int type = phone.getInt(contactTypeColumnIndex);
            String customLabel = "Custom";
            CharSequence phoneType = ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(), type, customLabel);
            contact.addNumber(number, phoneType.toString());
            phone.moveToNext();
        }
    }

    phone.close();
}

here is my mainActivity...

private static final int PERMISSIONS_REQUEST_READ_CONTACTS=100;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
        lvContacts = (ListView) findViewById(R.id.lvContacts);
        showContacts();
    }
private void showContacts(){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, PERMISSIONS_REQUEST_READ_CONTACTS);
        //After this point you wait for callback in onRequestPermissionsResult(int, String[], int[]) overriden method
    } else {
        listContacts = new ContactFetcher(this).fetchAll();
        ContactsAdapter adapterContacts = new ContactsAdapter(this, listContacts);
        lvContacts.setAdapter(adapterContacts);
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                       int[] grantResults) {
    if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Permission is granted
            listContacts = new ContactFetcher(this).fetchAll();
            ContactsAdapter adapterContacts = new ContactsAdapter(this, listContacts);
            lvContacts.setAdapter(adapterContacts);
        } else {
            Toast.makeText(this, "Until you grant the permission, we canot display the names", Toast.LENGTH_SHORT).show();
        }
    }
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}
1

There are 1 best solutions below

2
marmor On

The specific issue you have there is assuming that loadInBackground immediately returns a cursor with all the data in hand, that's not how it works.

But in general CursorLoader is not a recommended pattern to load stuff onto UI. You should do it differently, by querying for data inside an AsyncTask for example, and then delivering the results in the onPostExecute.

For the simplest changes from your existing code, I would change fetchAll to be a blocking method, like so:

public ArrayList<Contact> fetchAll() {
    String[] projectionFields = new String[]{
            ContactsContract.Contacts._ID,
            ContactsContract.Contacts.DISPLAY_NAME,
    };
    ArrayList<Contact> listContacts = new ArrayList<>();
    Cursor c = context.getContentResolver().query(Contacts.CONTENT_URI,projectionFields,null,null,null);

    final Map<String, Contact> contactsMap = new HashMap<>(c.getCount());

    if (c.moveToFirst()) {
        ... // same code
    }
    c.close();
    matchContactNumbers(contactsMap);
    return listContacts;
}

and call if from an AsyncTask so it won't run any heavy code on the UI thread, like so:

new AsyncTask<Void, Void, ArrayList<Contact>>() {
    @Override
    protected ArrayList<Contact> doInBackground(Void... params) {
        return new ContactFetcher(this).fetchAll();
    }
    @Override
    protected void onPostExecute(ArrayList<Contact> listContacts) {
        ContactsAdapter adapterContacts = new ContactsAdapter(this, listContacts);
        lvContacts.setAdapter(adapterContacts);         
    }
}.execute();