I am basing my code on the Login sample supplied with Android Studio. That sample contains code to populate an AutoCompleteTextView with email addresses related to the device's ContactsContract.Profile contact. i.e. the phone's owner, me.
I need to keep using the LoaderCallbacks interface methods - onCreateLoader() and onLoaderFinished().
I want to fetch extra details on the contact like:
phone number
given name
family name
To achieve this, I have tried adding extra fields to the ProfileQuery interface defined in the sample (that works correctly to fetch email address):
private interface ProfileQuery {
String[] PROJECTION = {
// these fields as per Android Studio sample
ContactsContract.CommonDataKinds.Email.ADDRESS,
ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
// these fields added by me
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
};
}
I have modified the onCreateLoader() method to remove the sample's WHERE clause in the hope to get the extra data:
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
return new CursorLoader(this,
// Retrieve data rows for the device user's 'profile' contact.
Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION,
// select all fields
null, null,
// Show primary email addresses first. Note that there won't be
// a primary email address if the user hasn't specified one.
ContactsContract.Contacts.Data.IS_PRIMARY + " DESC");
}
For what it is worth, at the moment my onLoadFinished() just logs the received data out:
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Log.d("xxx", cursor.getString(0) + cursor.getString(1) + cursor.getString(2) + cursor.getString(3) + cursor.getString(4));
cursor.moveToNext();
}
}
I would like each cursor row to give me a complete set of data relating to the Profile contact. Instead, I am getting seemingly random fields from that contact.
My CursorLoader construction is clearly wrong, but I do not know how to fix that.
How can I get the following details from my Profile contact:
email address
phone number
given name
family name?
The code above is based on a misunderstanding of how the contacts data is stored.
The Uri being used is pointing to the ContactsContract.Data table. The Developer docs explain the structure of this table. Each row in the table is an item of information, not a contact. e.g. an email address, a phone number etc.
The projection defined is specific to email addresses (since the sample deals only with emails). A more general projection that can handle different data types could be something like:
private interface ProfileQuery {
String[] PROJECTION = {
ContactsContract.Data.MIMETYPE,
ContactsContract.Data.DATA1,
ContactsContract.Data.DATA2,
ContactsContract.Data.DATA3,
};
}
As the docs explain:
DATA1 is an indexed column and should be used for the data element
that is expected to be most frequently used in query selections. For
example, in the case of a row representing email addresses DATA1
should probably be used for the email address itself, while DATA2 etc
can be used for auxiliary information like type of email address.
.
For example,
Phone.NUMBER is the same as Data.DATA1.
The query then returns a cursor row for each piece of information in the Profile contact. In my case, the 3 email addresses I stored, a phone number and my name.
For each row the Data.MIMETYPE column defines how the other data columns should be read. The mimetypes can be queried based on constants declared in ContactsContract.CommonDataKinds class, such as:
Email.CONTENT_ITEM_TYPE
Phone.CONTENT_ITEM_TYPE
StructuredName.CONTENT_ITEM_TYPE
Organization.CONTENT_ITEM_TYPE
Related
I've pulled out most of my hair now and really needs some help
before I go completely bold
I'm trying to launch an action picker to select a Contact that has a phone number.
When that contact is selected, I want to extract the name and phone number.
But this only happens for some contacts, not all.
The code is rougly as follows:
Select contact:
Intent contactPicker = new Intent(Intent.ACTION_PICK, contactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(contactPicker, REQ_PICK_CONTACT);
Extract id (notice getData().getLastPathSegment():
onContactForImportPicked(intent.getData().getLastPathSegment());
and then I try to fetch this contact:
String[] fields = new String[] {
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY
};
Cursor cursor = content.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
fields,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?", new String[] { id }, // SEE BELOW
null);
It this point, many contacts are fetched correctly, but a lot is
also non-existing. cursor.getCount() == 0. In the 'SEE BELOW'
section above, I've tried various other fields, linke
Contact._ID, Phone._ID etc etc.
Any idea why some contacts are not fetched with this method?
When I use your code, I don't even get the contact I selected correctly. The intent data contains the URI to the data you want, so you can use that directly.
Try --
Cursor cursor = content.query(data.getData(),
fields,
null,
null,
null);
I'm trying to get "notes" from a single contact. It added fine but retrieving it has been a problem.
String selection = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME+" like'%" + sender +"%'";
String[] projection = new String[] { ContactsContract.CommonDataKinds.Note.NOTE};
Cursor c2 = getContentResolver().query(ContactsContract.Data.CONTENT_URI, projection, selection, null, null);
if (c2.moveToFirst()) {
notes = c2.getString(0);
}
It works fine with other values like name or phone number but can't seem to get notes to retrieve correctly. It retrieves a random value like email instead.
I believe that your problem is that not all rows in the table represent contact types that have notes. You have to request the proper MIME Type.
ContactsContract.CommonDataKinds.Note is an alias for the 'data1' column that is present on all rows, so when you get a row of a different MIME Type, it represents different data.
How to get contacts in Android should give you an idea of how to do this.
I'm trying to use the implementation of the code found in this question post: How to read contacts on Android 2.0 but I can't figure out how to get it also run through the given, family, or display name columns. How can I get this implementation (the large one in the linked question) to give me the given and display names of the contacts as it loops through each row? I want to use this implementation specifically because it loops through the specified columns in each row and returns the information in the order it is in the row.
Here is the implementation from the other question that I'm referring to:\
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null, null, null, null);
while (cursor.moveToNext()) {
String contactId = cursor.getString(cursor.getColumnIndex(
ContactsContract.Contacts._ID));
String hasPhone = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
if (Boolean.parseBoolean(hasPhone)) {
// You know it has a number so now query it like this
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId, null, null);
while (phones.moveToNext()) {
String phoneNumber = phones.getString(phones.getColumnIndex( ContactsContract.CommonDataKinds.Phone.NUMBER));
}
phones.close();
}
Cursor emails = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId, null, null);
while (emails.moveToNext()) {
// This would allow you get several email addresses
String emailAddress = emails.getString(
emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
}
emails.close();
}
cursor.close();
First of all, the answer in the linked post is a bit obsolete, because there now is documentation for Contacts Provider at developer.android.com.
Second, the problem you're having is that you're querying the "data" table with a contact ID for the contacts table, and that won't work.
The Contacts Provider is a three-tiered arrangement of tables. The top level is the Contacts table, whose constants are defined in ContactsContract.Contacts. One of its columns is
ContactsContract.Contacts._ID, which identifies a contact row. HOWEVER, a row in this table is an aggregation of individual contacts from various sources.
The individual contacts are stored in ContactsContract.RawContacts. For each ContactsContract.Contacts._ID, there can be more than one row in ContactsContract.RawContacts.
For each row in ContactsContract.RawContacts, there are one or more rows in ContactsContract.Data. Each row has a MIME type that tells you what type of data it is. For example, a row in ContactsContract.RawContacts can have three rows in ContactsContract.Data that have the MIME type for phone numbers. Each of the three "data" rows is a different type of phone number (home, mobile, work) for the contact in ContactsContract.RawContacts.
You can see why looking for ContactsContract.Contacts._ID in ContactsContract.Data won't work; that's the wrong ID to look for.
Rather than re-write the documentation here, I suggest you take a look at it. It has some nice illustrations that help explain what I'm getting at:
Contacts Provider
I am using Filter with ListView which is populated trough Contact data which contains Names and Number.
Now i got two problems when i type a text into EditText which in turns fires adapter.getFilter().filter(s.toString())
1) When i type 'aa' latter (in my code )
i can see name starting from 'aa' for example aakruti , but at the
same time i am able to view email addresses too , which i don't wanted
to make it visible when a filter is fired.
2) When i type 'aa' latter (in phone's inbuilt contact list)
i can see name starting from 'aa' for example aakruti
but i am missing one name i.e. S A T Y A ( which is shown by contact
search when i type 'aa' latter into it )
here is my filter query , inside runQueryOnBackgroundThread
StringBuilder buffer = null;
String[] args = null;
if (constraint != null) {
buffer = new StringBuilder();
buffer.append("UPPER(");
buffer.append(ContactsContract.Contacts.DISPLAY_NAME);
buffer.append(") GLOB ?");
args = new String[] { constraint.toString().toUpperCase() + "*" };
}
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
System.out.println(buffer);
return mContent.query(
ContactsContract.Contacts.CONTENT_URI,
projection,
buffer == null ? null : buffer.toString(),
args,sortOrder
);
projection data
public static String[] projection = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME
};
EDIT
So far i tried to access
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
But GIVEN_NAME displays few email address too and even it shows contact name which has email address
for example ,
xyz#gmail.com
Raul Jakson (which only has email address no contact number)
Raul Jakson ( i see this name twice as it has two different email address , but i wanna see it as name )
so can anyone tell me how can i limit the email addresses and show only NAMES which has only contact phone numbers ?
Firstly, the emails show up because that contact has no name. In these cases, Android will use the email as the display name. To avoid showing these, use GIVEN_NAME and FAMILY_NAME. See the api docs for StructuredName.
Secondly, you don't find SATIYA as your query is looking for a DISPLAY_NAME that begins with AA. SATIYA is not a word, it's a sequence of initials, e.g. S A T I Y A. If you want to find these, then you'll have to craft your query to cater for this. You could search for *A*A*, but you'll probably get many others hits too. I suspect Android is doing some variant of an initial+surname search where BO would find Barack Obama.
When looking at the Contact Groups on Google Contacts or in the People application of my HTC Legend phone, I get the groups names ok eg: Friends, Family, VIP, Favorite etc...
But in my application I get really wrong names such as
"Family" became "System Group: Family"
"Friends" became "System Group: Friends"
"Favorite" became "Favorite_5656100000000_3245664334564"
I use the below code to read these values:
public Cursor getFromSystem() {
// Get the base URI for the People table in the Contacts content
// provider.
Uri contacts = ContactsContract.Groups.CONTENT_URI;
// Make the query.
ContentResolver cr = ctx.getContentResolver();
// Form an array specifying which columns to return.
String[] projection = new String[] {
ContactsContract.Groups._ID, ContactsContract.Groups.TITLE,
ContactsContract.Groups.NOTES
};
Cursor managedCursor = cr.query(contacts, projection, ContactsContract.Groups.DELETED
+ "=0", null, ContactsContract.Groups.TITLE + " COLLATE LOCALIZED ASC");
return managedCursor;
}
What I am missing?
That sounds like a bug. One of my test phones has correct/sanitized titles, while the other has that type of incorrect title. I'd file this here.
I also inspected the contacts2.db database directly, and found that the SYSTEM_ID column seems to be sanitized – but that's probably not safe to use for display purposes.