Search code examples
androidcontacts

Why is my contacts query giving a contact with a different id?


I have an Activity to allow the user to select contacts. Upon finish, the id of selected contacts is passed in the reply Intent. Where the result is processed, the details of selected contacts are to be read. The problem is that when processing the result the details read are for a different contact than was selected. I don't know why. In my case the user is only selecting one contact.

I've read through a lot of documentation and other questions regarding reading contacts, but not seen anything that helps my case.

From the Activity to get the user selection from contacts:

@Override
protected void onCreate (Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_select_from_contacts);

    m_view = findViewById(R.id.lv_contactsSelect);
    m_view.setVisibility(View.VISIBLE);

    getListView().setItemsCanFocus(false);
    getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

    Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
                new String[]{ContactsContract.Contacts._ID,
                        ContactsContract.Contacts.DISPLAY_NAME},
                null, null, "UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ") ASC");
    setListAdapter(new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_multiple_choice,
                cursor,
                new String[]{ContactsContract.Contacts.DISPLAY_NAME},
                new int[]{android.R.id.text1},
                0));

    Button btn = findViewById(R.id.btn_get_contacts);
    btn.setOnClickListener((View view) -> {
        Intent replyIntent = new Intent();
        ArrayList<Long> ids = pickContacts();
        replyIntent.putExtra(Activities.ARG_SELECTED, ids);
        setResult(Activities.RESULT_CONTACTS_SELECTED, replyIntent);
        finish();
    });
}

/** Viewer for list of contacts */
private ListView m_view;

private ListView getListView ()
{ return m_view; }

private CursorAdapter mAdapter;

private void setListAdapter (@NonNull CursorAdapter adapter)
{
    mAdapter = adapter;
    m_view.setAdapter(adapter);
}

// return id for each selected contact
private ArrayList<Long> pickContacts ()
{
    SparseBooleanArray a = getListView().getCheckedItemPositions();
    ArrayList<Long> contacts = new ArrayList<>();
    for (int i = 0; i < a.size(); i++)
    {
        if (a.valueAt(i))
        {
            Cursor c = (Cursor)mAdapter.getItem(a.keyAt(i));
            // TODO use RawContacts or Contacts? Currently the result is the same.
            //Long idContact = c.getLong(c.getColumnIndex(ContactsContract.Contacts._ID));
            Long idRaw = c.getLong(c.getColumnIndex(ContactsContract.RawContacts._ID));
            contacts.add(idRaw);
        }
    }
    return contacts;
}

Processing the result:

@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
    if (resultCode == Activities.RESULT_CONTACTS_SELECTED)
    {
        ArrayList<Long> ids = (ArrayList<Long>)data.getSerializableExtra(Activities.ARG_SELECTED);
        for (long id : ids)
        {
            getContactDetails(id);
        }
    }
}

/**
 * As mentioned in https://developer.android.com/reference/android/provider/ContactsContract.RawContacts
 * the best way to read a raw contact along with associated data is by using the Entity directory.
 */
// FIXME: The id from the received result matches what was selected,
// but this function reads details for a different contact.
private void getContactDetails (long rawContactId)
{
    System.out.println("Get contact with id " + rawContactId);
    Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
    Uri entityUri = Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY);
    // For example, this output can be "Get contact from entity uri content://com.android.contacts/raw_contacts/615/entity"
    // (Where 615 is the id for the selected contact.)
    System.out.println("Get contact from entity uri " + entityUri);
    Cursor c = getContentResolver().query(entityUri,
            new String[]{RawContacts.SOURCE_ID, Entity.DATA_ID, Entity.MIMETYPE, Entity.DATA1}, // projection
            null, null, null);
    if (c == null)
        return;
    try
    {
        while (c.moveToNext())
        {
            // In this example I'm just dumping data to the console.
            if (!c.isNull(1))
            {
                String mimeType = c.getString(2);
                String data = c.getString(3);
                System.out.println("mimeType = " + mimeType);
                System.out.println("data = " + data);
            }
        }
    }
    finally
    {
        c.close();
    }
}

For example, the console output from the handler includes:

mimeType = vnd.android.cursor.item/name
data = A name

Where the name is not the same one as selected in the contact selection activity.


Solution

  • in method pickContacts, change it to get the Contacts._ID, it finds an ID by chance, because both RawContacts._ID and Contacts._ID are both the string "_id", but it's just plain wrong.

    Next, since you're actually grabbing a contact-id, you need to modify your getContactDetails to accept a ContactId and not a RawContactId.

    Not sure why you need to involve Entity APIs like that, if you only need to query for that contact's data, do this:

    private void getContactDetails (long contactId) {
        Log.i("Contacts", "Get contact with id " + contactId);
    
        String[] projection = new String[]{Data.DISPLAY_NAME, Data.MIMETYPE, Data.DATA1};
        String selection = Data.CONTACT_ID + "=" + contactId;
        Cursor c = getContentResolver().query(Data.CONTENT_URI, projection, selection, null, null);
        if (c == null) {
            return;
        }
        try {
            while (c.moveToNext()) {
                String name = c.getString(0);
                String mimeType = c.getString(1);
                String data = c.getString(2);
    
                Log.i("Contacts", contactId + ", " + name + ", " + mimetype + ", " + data);
            }
        } finally {
            c.close();
        }
    }