Reputation: 301
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.
Upvotes: 0
Views: 359
Reputation: 28239
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();
}
}
Upvotes: 2