I'm trying to retrieve data from the Contact provider but the data shows seems not query correctly. The code I use is just below:
mCursor = mContext.getContentResolver().query(
ContactsContract.Data.CONTENT_URI, null, null, null, null);
if(mCursor == null){
mListener.onRetrieveError();
return;
}
for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) {
String contact_f = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
String contact_m = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
String contact_l = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
String phone_type = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
String email_type = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
Log.d(TAG, "|" + contact_l + ", " + contact_f + " " + contact_m + " | " + phone_type + " | " + email_type);
}
The Log shows for example :
Log: |LastName, FirstName | FirstName | FirstName
Log: |null, 3 null | 3 | 3
I was expecting :
Log: |LastName, FirstName MiddleName | PhoneType| EmailType
I want to be able to get the firstname, lastname, middle name, email type (home/work...) - and all email types listed and also the phone type.
The goal is to for example get for a contact his lastname, firstname middle name and also saying that we got is home and work phone or email
The result I got seems a mix.
Any ideas.
Regards
That's not how it works. The Data table contains one row per contact value field (phone number, email address, structured name ...). Each row has a mimetype value that tells you how to interpret the columns of that row.
For example, if the mimetype column has the value Phone.CONTENT_ITEM_TYPE you must use the Phone column mapping to access the data.
So if a contact has one phone number and one email address there are at least 3 rows for this contact in the Data table. One for the StructuredName (this one is required exactly once for each RawContact), one for the Phone number and one for the Email address.
Each row also has a RAW_CONTACT_ID that contains the row id of the RawContact the value belongs to.
When you query the Data table, you actually read from a view that joins the real Data table with the RawContacts table, so each row also contains the CONTACT_ID of the contact the row belongs to. That means if two rows have the same CONTACT_ID they belong to the same contact.
I hope that explains it. I don't really know what you're trying to achieve in the end, so I can't give any helpful code snippets.
Update
The key to this is to check the mimetype of each row before you decide how to interpret the values. Also you need to read the CONTACT_ID or RAW_CONTACT_ID to be able to aggregate the data correctly. If you order the result by CONTACT_ID you can assume that all rows belonging to a contact have been read when the CONTACT_ID value changes.
The (untested) code snippet below should point you to the right direction. The point is, you can not get all the data of a contact in a single row. You always need to read multiple rows and aggregate them in some way.
mCursor = mContext.getContentResolver().query(
ContactsContract.Data.CONTENT_URI, null, null, null, ContactsContract.Data.CONTACT_ID);
if(mCursor == null){
mListener.onRetrieveError();
return;
}
colIdxContactId = mCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
colIdxMimetype = mCursor.getColumnIndex(ContactsContract.Data.MIMETYPE);
long lastContactId = -1L;
for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext())
// this is the contact Id the current row belongs to
long contactId = mCursor.getLong(colIdxContactId);
if (lastContactId != contactId) {
// the previous contact is complete, the following data belong to a new contact
// handle this case ...
lastContactId = contactId;
}
// the mimetype column tells us how to interpret the current row:
switch(mCursor.getString(colIdxMimetype)) {
case ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE: {
// this row is a structured name
String contact_f = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
String contact_m = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
String contact_l = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
// store names somewhere ...
break;
}
case ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE: {
// this row represents a phone number
int phone_type = mCursor.getInt(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
// store phone_type somewhere ...
break;
}
case ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE: {
// this row represents an email address
int email_type = mCursor.getInt(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
// store email_type somewhere ...
break;
}
}
}
You can use this
public void getAllContacts(){
Cursor cursor = null;
ContactPerson contact;
try {
cursor = getActivity().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
int nameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int phoneNumberIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int emailAddressIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
cursor.moveToFirst();
do {
contact = new ContactPerson(cursor.getString(nameIdx),cursor.getString(phoneNumberIdx),cursor.getString(emailAddressIdx));
cpList.add(contact);
// Toast.makeText(getActivity(),cursor.getString(nameIdx)+" "+ cursor.getString(phoneNumberIdx),Toast.LENGTH_SHORT).show();
} while (cursor.moveToNext());
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(getActivity(),e.getMessage(), Toast.LENGTH_LONG).show();
Log.d("Error",e.getMessage());
} finally {
if (cursor != null) {
cursor.close();
}
}
}
I think the URI you are using is wrong.
Try using ContactsContract.Contacts.CONTENT_URI instead of ContactsContract.Data.CONTENT_URI (this is what I use).
Related
private void displayAllContactsByType(String accountName) {
Cursor rawCursor = null;
try {
rawCursor = mCProviderClient.query(
ContactsContract.RawContacts.CONTENT_URI,
null,
ContactsContract.RawContacts.ACCOUNT_NAME + "= ?",
new String[]{accountName},
null);
} catch (RemoteException e) {
e.printStackTrace();
}
int contactIdColumn = rawCursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID);
Utils.Log("Raw Size", " " + rawCursor.getCount());
while (rawCursor.moveToNext()) {
String contactId = rawCursor.getString(contactIdColumn);
// Utils.Log("contactId ",contactId);
storeContactDetails(contactId);
}
if (contactList != null) {
contactListener = (ContactListener) context;
contactListener.updateView(contactList);
}
}
I am getting all the account name which raw contacts contain. And I would like to get all the contacts belongs to the particular account name but I am getting null contact id. How can I get all the contact id to a particular account name and then respective data of that contact id along with it?
First, RawContacts.CONTACT_ID is of type Long, not String, so you should do:
Long contactId = rawCursor.getLong(contactIdColumn);
See: https://developer.android.com/reference/android/provider/ContactsContract.RawContactsColumns.html#CONTACT_ID don't pay attention to the type of the CONTACT_ID field which are always Strings, look at Type: INTEGER below it.
Second, this is rare, but can happen when a RawContact does not belong to any Contact, I call those zombie RawContacts, they can be created either via a corrupt DB or a bug, or intentionally using AGGREGATION_MODE when creating the RawContact, see AGGREGATION_MODE_DISABLED
I'm new to android and i'm working with native contact.
So my app is let user put contact display name and their number for edit/delete.
In case the contact have more that one number.
I tried a lot but still have no luck, the app still doesn't update the number or it crashes.
What I'm going to do as my understanding is:
Find name in contact that matched name user inserted and use that to get contact_id that represent this contact datagroup.
Use contact_id in 1. and the number user input to find ._ID that represent the specific row id.
Do task with ._ID we get from 2.
This is 1. code to get contact_id:
public String getPeopleUniqueID(String name, Context context) {
String s = null;
String selection = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME+" like'%" + name +"%'";
String[] projection = new String[] {ContactsContract.Data.CONTACT_ID};
Cursor c = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection, selection, null, null);
if(c.moveToFirst()) {
s = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
}
c.close();
return s;
}
This is 2. code to get ._ID (num is number user inserted and name is from 1. > the contact_id)
public String checkPhoneNumber(String num, String name, Context context) {
String s = null;
String selection = ContactsContract.CommonDataKinds.Phone.NUMBER + "=?" + " AND "+ContactsContract.Data.CONTACT_ID+ "=?";
String[] projection = new String[] {ContactsContract.Data._ID};
Cursor c = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection, selection, new String[]{u,name}, null);
if(c.moveToFirst()) {
s=c.getString(c.getColumnIndex(ContactsContract.Data._ID));
}
c.close();
if (s==null){
s = "null";
}
return s;
}
To do something like editing (num is _.ID we get from 2. and newnum is new number user want to change into).
public void editNumber(String num , String newnum) {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
.withSelection(Data._ID + "=? AND " +
Data.MIMETYPE + "='" +
CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'",
new String[]{num})
.withValue(Data.DATA1, newnum)
.build());
try{
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);}
catch (RemoteException e){e.printStackTrace();}catch (OperationApplicationException e) {e.printStackTrace();}
}
And well it crashes when I call editNumber().
Can you help me fix my code and my understanding?
And another question, can I edit/insert group for the contact programatically, like I want to add this contact to family friend or co-worker group (the default group that we can set at contact edit page)?
Use ContactsContract.Contacts.CONTENT_FILTER_URI for searching a contact based on name - to get Id or anything else. The like operator cannot handle all cases which the CONTENT_FILTER_URI does handle - For various languages, special characters etc.
http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html#CONTENT_FILTER_URI
Use following uri to lookup a contact from phone number - you can get person id or anything else :
Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(phoneNumber));
In the set query you can also use contactId in the condition
For groups you can use custom mimetypes if the default one does not suit you (which is still very primitive for groups across different account types)
Is there a faster method for reading contacts in android? For example my method with cursor take 3-5 seconds for reading 30-50 contacts. It's very long.
Cursor cursor = managedQuery(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 ( hasPhone.equalsIgnoreCase("1"))
hasPhone = "true";
else
hasPhone = "false" ;
if (Boolean.parseBoolean(hasPhone))
{
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,null, null);
while (phones.moveToNext())
{
names.add(cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME)));
numbers.add(phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
}
phones.close();
}
}
Any ideas?
your question is interesting....
reading a fast contacts because it take time to read contacts data from ContactsContract..
i don't know about another way then the one you use, but still u can increase the performance by providing a String[] projection parameter to managedQuery...
fetch only those data which u require from contactsContract, because by providing null value it fetches all the columns of the record.
Sorry for my bad english. I have created the similar but with different style using HashMap. I will paste my approach.
//Create a hashMap, Key => Raw Contact ID and value => Object of customClass
HashMap<Long, ContactStructure> mFinalHashMap = new HashMap<Long, ContactStructure>();
//Run IN query in data table. example
select mimetype_id, raw_contact_id, data1 to data14 from data where raw_contact_id IN (select _id from raw_contacts where deleted <> 1 and account_type = "phone" and account_name = "bla bla") and mimetype_id = (select _id from mimetypes where mimetype = "vnd.something.phone");
Now Create a class which will have all the data of contact.
while accessing the cursor.
while (cursor.moveToNext()) {
ContactStructure contactStructure = mFinalHashMap.get(rawContactID);
//It will return the previous instance of object, If we already put
if(rawContactStructure == null) {
contactStructure = ContactStructure.provideInstance();
}
//Now check for your required mimeType
case MIMETYPE_PHONE:
contactStructure.hasPhoneNo = true;
contactStructure.phoneNumbers.add(addDetail); //add the data1 .. to data14
break;
}
/*Demo class for saving the details*/
public class ContactMetaData {
static classContactStructure {
boolean hasPhoneNo;
List<List<String>> phoneNumbers;
public static ContactStructure provideInstance() {
contact.phoneNumbers = new ArrayList<List<String>>();
ContactStructure contact = new RawContactStructure();
return contact
}
}
use this approach, I tried with 3000 contacts with all the data, It was fast. coz its not easy to get all the contacts and their all data with in sec..
For reading contacts faster you need to use the concept of the projection
where you specify only columns which you need to fetch.
cursor.moveToFirst();
while (cursor.isAfterLast() == false) {
String contactNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
int phoneContactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID));
int contactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));
Log.d("con ", "name " + contactName + " " + " PhoeContactID " + phoneContactID + " ContactID " + contactID)
cursor.moveToNext();
}
This will help you to reduce the timing by 90% for complete tutorial i used this site http://www.blazin.in/2016/02/loading-contacts-fast-from-android.html
Alas I have about 500 contacts in my phone book and for some reason after synch'ing them with thunderbird the display name is random last, first...first last. So I thought I would put a quick widget together to just re-do al my display names to last, first. The code I use is below, however I am not getting last / first values. The keys in the cursor exist (data1, data2), but the values were "1" and null respectively. Any Ideas?
Cursor cursor = getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
while (cursor.moveToNext() != false) {
String id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID));
String fname = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
String lname = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
if (lname != null && lname.length() > 0) {
String sDName = lname + "," + fname;
ContentValues values = new ContentValues();
values.put(ContactsContract.Contacts.DISPLAY_NAME, sDName);
getContentResolver().update(ContactsContract.Data.CONTENT_URI, values, ContactsContract.Contacts._ID+"=", new String[] {id});
}
}
ContactsContract.Data contains all various types of information like postal address, phone numbers, e-mail, web sites, photes etc. in a shared table.
You have to filter out the rows that contain the information you are interested in.
In the query, add a WHERE clause:
query(ContactsContract.Data.CONTENT_URI, Data.MIMETYPE + "=?",
new String[]{StructuredName.CONTENT_ITEM_TYPE}, null, null);
See the documentation for ContactsContract.Data
I want to get the nickname of a contact from addressbook. I start with his phone number, query it and want the nickname (aka alias) as a result.
Cursor cur = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.NUMBER + " = " + incomingNumber, null, null);
if (cur.moveToFirst()) {
Log.e("saymyname", cur.getString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME)));
Log.e("saymyname", cur.getString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.LABEL)));
}
Output of the logs is the incomingNumber (first Log.e() ) and null (second Log.e() ), but I want to get the contact's nickname!
Thanks
Tom
Nickname is held in a different table than the phone numbers, you have to query ContactsContract.Data.CONTENT_URI
Check my answer on this question
(I don't have necessary reputation to comment so I have to add answer)
TomTasche's answer is misleading and it got me to waste a lot of time trying to figure out why I couldn't get the proper nickname on a contact I knew had one.
I found the answer by myself but got the confirmation from this post that I was now doing it properly.
Basically when you read the ContactsContract.Data documentation you read:
Each row of the data table is typically used to store a single piece of contact information (such as a phone number) and its associated metadata (such as whether it is a work or home number).
That explains the shady part of TomTasche's code :
if (nickname.equals(incomingNumber)) {
return name;
}
Since doing a search with just the CONTACT_ID can return multiple rows (one for each information type), it's not guaranteed that the first one contains the nickname. When there is a nickname it will be in DATA1, but the phone number is also found in DATA1.
The example part in the documentation of ContactsContract.Data makes it clear that rows must be selected based on their MIME_TYPE, only when the MIME_TYPE is selected, can we start making sense of the content in the row itself.
A proper query would therefore be:
cursor = getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
new String[]{Nickname.NAME},
ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + "= ?",
new String[]{contactID, Nickname.CONTENT_ITEM_TYPE},
null);
(where Nickname is ContactsContract.CommonDataKinds.Nickname)
I felt I had to say something on this topic to prevent other people wasting as much time as I did based on this sole topic (first one I found with my Google friend).
The answer from Pentium10 was very helpful! Thanks!
If anybody needs a sample, look at the following code:
public String accessContact(String incomingNumber) {
if (incomingNumber == null || "".equals(incomingNumber)) {
return "unknown";
}
try {
Cursor cur = context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.DISPLAY_NAME, Phone.TYPE, Phone.CONTACT_ID}, Phone.NUMBER + " = " + incomingNumber, null, null);
int nameIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int typeIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
int idIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
String name;
String type;
String id;
if (cur.moveToFirst()) {
name = cur.getString(nameIndex);
type = cur.getString(typeIndex);
id = cur.getString(idIndex);
} else {
return "unknown";
}
cur = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, new String[] {ContactsContract.Data.DATA1}, ContactsContract.Data.CONTACT_ID + " = " + id, null, null);
int nicknameIndex = cur.getColumnIndex(ContactsContract.Data.DATA1);
String nickname;
if (cur.moveToFirst()) {
nickname = cur.getString(nicknameIndex);
if (nickname.equals(incomingNumber)) {
return name;
}
return nickname;
} else {
return name;
}
} catch (Exception e) {
e.printStackTrace();
return "unknown";
}