0

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;
}
marmor
  • 27,641
  • 11
  • 107
  • 150
  • Is there a typo in `new String[]{Manifest.permission.READ_CONTACTS}, PERMISSIONS_REQUEST_READ_CONTACTS`? It should be `new String[]{Manifest.permission.READ_CONTACTS, PERMISSIONS_REQUEST_READ_CONTACTS}`, where the curly bracket should be outside the 2nd string. – Edric Jan 08 '19 at 13:24
  • 1
    @Edric - No it shouldn't. The second constant is the request code. – PPartisan Jan 08 '19 at 13:29
  • Whoops, didn't see that! Thanks for pointing that out. – Edric Jan 08 '19 at 13:31

1 Answers1

0

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();
marmor
  • 27,641
  • 11
  • 107
  • 150
  • should i mention the Asyntask in mainActivity ? If so where should i check the Request permission condition? – prem kumar Jan 08 '19 at 13:50
  • you can put the AsyncTask code in your main activity as a new method `fetchContactsAsync`, and call it from both `showContacts` and from `onRequestPermissionsResult` instead of your current code that calls `fetchAll` (those 3 lines) – marmor Jan 08 '19 at 14:13