How do I update the display name for a contact? The operation in the code below completes without throwing anything and appears to work - that is, when I requeried the ContactsContract.Contact table, a row came back with the name changed. However, when I tried running the stock "people" app on my tablet, it crashed. Evidentally I did something wrong.
Here is the code. Early on, it fetches an id from the aggregate contacts as follows, where key is the lookup_key:
String[] projection = new String[] {
Contacts._ID, // 0
Contacts.DISPLAY_NAME, // 1
};
Uri uri = Uri.parse (Contacts.CONTENT_LOOKUP_URI + "/" + key);
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query (uri, projection, null, null, null);
if (!cursor.moveToNext()) // move to first (and only) row.
throw new IllegalStateException ("contact no longer exists for key");
origId = cursor.getLong(0);
cursor.close();
Then, after the user has done his edits, I call this block of code to update the display_name:
ArrayList<ContentProviderOperation> opers = new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;
String[] args = { Long.toString (origId) };
builder = ContentProviderOperation.newUpdate (Data.CONTENT_URI);
builder.withSelection (RawContacts.CONTACT_ID + "=?", args);
builder.withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, name);
opers.add(builder.build());
ContentProviderResult[] results = null;
try {
results = getContentResolver().applyBatch(ContactsContract.AUTHORITY, opers);
} catch ...
I realize I don't need the ContentProviderOperation for this example; that's for later when I have more stuff to update.
To be honest, I'm pretty confused about which ID I'm actually using. The names aren't that clear to me and I may be using the wrong ID for this operation.
For what it's worth, looking at results after the update I saw a result code of 5. I can't find any documentation for that, so have no idea if that is significant.
The IDs (and altering contacts in general) can be pretty confusing... I had some dramas getting my head around them as well.
Here is some working code I use for updating. The main difference I can see is how you are declaring the raw ID; it needs to be included in as a content value.
Cursor cursor = _context.getContentResolver().query(contactUri,
new String[] { Contacts._ID }, null, null, null);
try {
if (cursor.moveToFirst()) {
String rawContactId = cursor.getString(0);
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ContentValues contentValues = new ContentValues();
contentValues.put(Data.RAW_CONTACT_ID, rawContactId);
contentValues
.put(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
contentValues.put(
ContactsContract.CommonDataKinds.Phone.NUMBER,
phoneNumber);
contentValues.put(ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_WORK);
ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
.withValues(contentValues).build());
String contactId = contactUri.getLastPathSegment();
ops.add(ContentProviderOperation
.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(
ContactsContract.Data.CONTACT_ID
+ "=? AND "
+ ContactsContract.Data.MIMETYPE
+ "=?",
new String[] {
contactId,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE })
.withValue(
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
newName).build());
result = _context.getContentResolver().applyBatch(
ContactsContract.AUTHORITY, ops);
}
} finally {
cursor.close();
}
hopefully it helps!
Answer above is generally correct. However, when I inserted
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME
and then tried to do newUpdate
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME
with code as above - it made contact displayed in Contacts app with name mixed of old and new data. I found out inserting and updating ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME for example works as I expect. In Contacts app on my Android 4x when editing contact I cannot see family and given separately, look to me DISPLAY_NAME is made of them by Android.
Related
I first do a request like that :
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String[] projection = new String[]{
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.PHOTO_ID,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.CommonDataKinds.Phone.NUMBER
};
Cursor cursor = resolver.query(uri, projection, null, null, null);
ArrayList<Contact> contactList = new ArrayList<> ();
if (cursor != null && cursor.moveToFirst ()) {
do {
Contact contact = new Contact();
contact.Id = cursor.getLong (cursor.getColumnIndex (projection [0]));
contact.DisplayName = cursor.getString (cursor.getColumnIndex (projection [1]));
contact.PhotoId = cursor.getString (cursor.getColumnIndex (projection [2]));
contact.PhoneNumber = cursor.getString (cursor.getColumnIndex (projection [4]));
contactList.add (contact);
} while (cursor.moveToNext());
cursor.close();
}
return contactList;
Then I use the ID returned by this first request to update a contact like so :
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
// Name
ops.add(ContentProviderOperation
.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(
ContactsContract.Contacts._ID
+ "=? AND "
+ ContactsContract.Data.MIMETYPE
+ "=?",
new String[] {
String.valueOf(contact.Id),
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.Contacts.DISPLAY_NAME, contact.DisplayName)
.build());
try {
resolver.applyBatch(ContactsContract.AUTHORITY, ops);
} catch (RemoteException e) {
e.printStackTrace();
} catch (OperationApplicationException e) {
e.printStackTrace();
}
But unfortunately the update part is not working.
Any idea of how to use result from the first request with the update request ?
You can't update a field from the Contacts table by updating the Data table.
All the fields in your projection that come from Contacts.X are added via an implicit join, but actually belong to a different table.
If I understand correctly, you're trying to update a contact's display-name, here's how to do it (make sure you import all classes from ContactsContract):
ops.add(ContentProviderOperation
.newUpdate(Data.CONTENT_URI)
.withSelection(Data.CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", new String[] { String.valueOf(contact.Id), StructuredName.CONTENT_ITEM_TYPE })
.withValue(StructuredName.DISPLAY_NAME, contact.DisplayName)
.build());
Also, FYI, you don't need to use cursor.getColumnIndex in your first query, just do:
contact.Id = cursor.getLong(0);
contact.DisplayName = cursor.getString(1);
contact.PhotoId = cursor.getString(2);
contact.PhoneNumber = cursor.getString(4);
Docs
Some columns from the associated raw contact are also available
through an implicit join. The other columns are excluded as
uninteresting in this context.
...
The ID column for the associated aggregated contact table
ContactsContract.Contacts is available via the implicit join to the
ContactsContract.RawContacts table, see above. The remaining columns
from this table are also available, through an implicit join. This
facilitates lookup by the value of a single data element, such as the
email address.
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)
I am looking to get the RAW_CONTACT_ID of a specific Contact using PhoneLookup or even just the Contacts LookupKey.
I know the contacts table has a column name_raw_contact_id that references the raw_contacts._id column but it doesn't seem to be returned when querying ContactsContract.Contacts.CONTENT_LOOKUP_URI with the contacts lookup key.
My phone lookup query is:
String[] projection = new String[] {
PhoneLookup.DISPLAY_NAME, PhoneLookup.LOOKUP_KEY };
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
Cursor c = resolver.query(uri, projection, null, null, null);
Then I am looking up the Contact based on the lookup key:
String[] contactProjection = new String[] {
ContactsContract.Contacts.NAME_RAW_CONTACT_ID
};
Uri contactUri = Uri.withAppendedPath(
ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey);
Cursor contactCursor = resolver.query(contactUri,
contactProjection, null, null, null);
However, this doesn't compile and I get
cannot find symbol: variable NAME_RAW_CONTACT_ID
location: class android.provider.ContactsContract.Contacts
But the android documentation shows NAME_RAW_CONTACT_ID as a column.
Is there any way I can get the RAW_CONTACT_ID based off either phone number or lookup key?
I found that the answer is to make a third query:
long rawContactId = -1;
Cursor c = getContentResolver().query(RawContacts.CONTENT_URI,
new String[]{RawContacts._ID},
RawContacts.CONTACT_ID + "=?",
new String[]{String.valueOf(contactId)}, null);
try {
if (c.moveToFirst()) {
rawContactId = c.getLong(0);
}
} finally {
c.close();
}
But it should be noted that there can be multiple RawContact's per one Contact and the above query will get ALL RawContact's associated with the contactId
ContactsContract.Contacts.NAME_RAW_CONTACT_ID column needs API level 21 or greater so make sure your are compiling with this version.
Also you can use ContactsContract.PhoneLookup._ID column in your first query to get the Contact_Id and then use this Contact_Id in your 3rd query so your problem will be solved in 2 queries instead of 3.
private HashSet<Long> getRawContactIdsForContact(long contactId)
{
HashSet<Long> ids = new HashSet<Long>();
Cursor cursor = context.getContentResolver().query(RawContacts.CONTENT_URI,
new String[]{RawContacts._ID},
RawContacts.CONTACT_ID + "=?",
new String[]{String.valueOf(contactId)}, null);
if (cursor != null && cursor.moveToFirst())
{
do
{
ids.add(cursor.getLong(0));
} while (cursor.moveToNext());
cursor.close();
}
return ids;
}
I created an app. It is working on htc desire hd, but when i tested it on ZTE Blade, a strange problem appeared. In my app when user selects a contact name from a spinner a menu appears. In the menu the user can send sms to the user, call him/her or just look at his/her contact info. On HTC Desire HD, everything is working fine. On ZTE there seems to be an exasperating problem with the contact info button: In certain cases when user selects a contact and wants to see his contact info, some other contact's info is shown. So I select Pete from my spinner but I get Dave's contact info. In other cases I select Tom from the spinner and I get Tom's contact info. The problem is not existing on my HTC. I couldn't figure out what causes the problem. By the way, my contact list on HTC is populated also from gmail and facebook and the app still working fine, while the contact list of the ZTE has never seen any gmail or facebook accounts (i am not entirely sure about this).
This is the code i am using to get to the contact info:
infobtn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cur.getCount() > 0)
{
while (cur.moveToNext()) {
id_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (name_contact.equals(name1))
{
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?", new String[]{id_contact}, null);
id_contact2 = id_contact;
while (pCur.moveToNext()){
}
pCur.close();
}
}
Intent intent_contacts = new Intent(Intent.ACTION_VIEW, Uri.parse("content://contacts/people/" + id_contact2));
startActivity(intent_contacts);
}
}
});
I have similar problems in my application. The app is a widget that give possibility to use favorites contacts in fast way. User creates new widget and assigns contact to it. When he clicks on widget he is able to make common contact actions - call, sms, edit, etc.
Some users send report like this one:
"I select about 4 contacts and the app shows 4 but only one of them right 3 others is same contact which I didnt even picked".
"Why does the wrong contact show when picked? On my droid incredible"
I can't reproduce the problem on my side. I have tried several different devices, tried to make import/export contacts - no results.
Anyway, i have some ideas about source of the problem.
My application uses lookup keys to store contacts, according to sdk. So, when user picks up a contact, application stores contact lookup key. Then it uses this lookup key to get Contact ID for this contact. After that, it uses ONLY received Contact ID in all other functions. So does your function - it uses only Contact ID in sub-query.
The question is: is it possible that different contacts (with different lookup keys) have same Contact ID? It looks like it's possible on some devices in rare cases... If so, we need to use Contact ID + lookup key always together to identify contact. (update: the suggestion was incorrect. Lookup key can be changed after contact info modification, you won't find contact).
As I understand, you are able to reproduce the problem on your side. I would suggest to change your code in such way:
infobtn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cur.getCount() > 0)
{
while (cur.moveToNext()) {
id_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String lookup_key = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
if (name_contact.equals(name1))
{
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ? AND " + ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY + " = ?", new String[]{id_contact, lookup_key}, null);
id_contact2 = id_contact;
while (pCur.moveToNext()){
}
pCur.close();
}
}
Intent intent_contacts = new Intent(Intent.ACTION_VIEW, Uri.parse("content://contacts/people/" + id_contact2));
startActivity(intent_contacts);
}
}
});
and try to reproduce the problem again.
You can see this link, It's very good: http://www.vtutorials.info/2014/08/working-with-android-contacts-how-to.html
private Map<Long, ArrayList<LocalContactInfo>> queryAllPhones(ContentResolver contentresolver)
{
HashMap<Long, ArrayList<LocalContactInfo>> hashmap = new HashMap<Long, ArrayList<LocalContactInfo>>();
Uri PhoneCONTENT_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String Phone_CONTACT_ID = ContactsContract.CommonDataKinds.Phone.CONTACT_ID;
String NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;
Cursor cursor = contentresolver.query(PhoneCONTENT_URI, null, null, null, null);
do
{
if (!cursor.moveToNext())
{
cursor.close();
return hashmap;
}
long l = cursor.getLong(cursor.getColumnIndex(Phone_CONTACT_ID));
String s = cursor.getString(cursor.getColumnIndex(NUMBER));
LocalContactInfo temp = new LocalContactInfo(String.valueOf(l), "", s, "", 1);
if (hashmap.containsKey(Long.valueOf(l))){
((List<LocalContactInfo>)hashmap.get(Long.valueOf(l))).add(temp);
} else{
ArrayList<LocalContactInfo> arraylist = new ArrayList<LocalContactInfo>();
arraylist.add(temp);
hashmap.put(Long.valueOf(l), arraylist);
}
} while (true);
}
And:
public List<LocalContactInfo> doloadContacts() {
List<LocalContactInfo> _return = new ArrayList<LocalContactInfo>();
Uri CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
String _ID = ContactsContract.Contacts._ID;
String DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME;
String HAS_PHONE_NUMBER = ContactsContract.Contacts.HAS_PHONE_NUMBER;
ContentResolver contentResolver = mContext.getContentResolver();
Map<Long, ArrayList<LocalContactInfo>> map = queryAllPhones(contentResolver);
Cursor cursor = contentResolver.query(CONTENT_URI, null,null, null, null);
if(cursor == null || cursor.getCount()==0){
return _return;
}
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
Long contact_id = cursor.getLong(cursor.getColumnIndex( _ID ));
String fullname = cursor.getString(cursor.getColumnIndex( DISPLAY_NAME )).trim();
int hasPhoneNumber = Integer.parseInt(cursor.getString(cursor.getColumnIndex( HAS_PHONE_NUMBER )));
if (hasPhoneNumber > 0 && map.get(contact_id) != null){
Object obj = ((List<LocalContactInfo>)map.get(contact_id)).get(0);
if(obj instanceof LocalContactInfo){
LocalContactInfo _temp = (LocalContactInfo)obj;
_return.add(new LocalContactInfo(String.valueOf(contact_id), fullname, _temp.getNumberPhone(), "",hasPhoneNumber));
}
}
}
}
return _return;
}
instead of using "contacts/people/xxx", try using contactsContract.Contacts.CONTENT_URI.
basically, you want to replace.
Uri contactUri = Uri.parse("content://contacts/people/" + id_contact2));
with
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,
id_contact2);
Another thing to note is that "id_contact2" may be changed if a sync occurs before you use it (like when google sync merges a contact). To prevent this, it might be better to use a LookupKey instead.
ContactsContract.Contacts.LOOKUP_KEY
and retrieve using
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI,
yourContactLookupKey);
I'm trying to add Data record to an already exist contact, I find the contact using phone lookup, i take the contact _id field, and add a new data with raw_contact_id set to the _id field.
on some contacts it just doesn't work, it match the data to different contact.
(I think it relates to contacts that are stored on the sim card)
Please advice, maybe you have a different way to add the data
code sample:
LinkedList<Long> lcv = new LinkedList<Long>();
ContentResolver cr = getContentResolver();
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor c = cr.query(uri, null, null, null, null);
try {
while (c.moveToNext()) {
Uri lookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
c.getString(c.getColumnIndex(PhoneLookup.LOOKUP_KEY)));
Cursor c2 = getContentResolver().query(lookupUri, new String[] { Contacts._ID, Contacts.DISPLAY_NAME },
null, null, null);
try {
if (c2.moveToNext()) {
Log.i(TAG, "found: " + c2.getLong(c2.getColumnIndex(Contacts._ID)) + ", " + c2.getString(c2.getColumnIndex(Contacts.DISPLAY_NAME)));
lcv.add(c2.getLong(c2.getColumnIndex(Contacts._ID)));
} else {
Log.e(TAG, "failed to lookup");
}
} finally {
c2.close();
}
}
} finally {
c.close();
}
for (Long rawid : lcv) {
Cursor c3 = cr.query(RawContacts.CONTENT_URI, null, RawContacts.CONTACT_ID + "=?", new String[] {rawid+""}, null);
if (c3.moveToNext()) {
Log.e(TAG,"aaaa: " + c3.getString(c3.getColumnIndex(Contacts.DISPLAY_NAME)));
} else {
Log.e(TAG,"errrrror");
}
ContentValues cv = new ContentValues();
cv.put(Data.RAW_CONTACT_ID, rawid + "");
cv.put(Data.MIMETYPE, MyMime.MIMETYPE);
cv.put(Data.DATA1, "mydata");
cv.put(Data.SYNC1, syncvalue);
Uri newIns = cr.insert(ContactsContract.Data.CONTENT_URI, cv);
Log.i(TAG, "insert: " + newIns + ", " + name);
}
The problem lies when you select the Contacts._ID and use this id to populate the data in the LinkedList lcv .
Cursor c2 = getContentResolver().query(lookupUri, new String[] { Contacts._ID, Contacts.DISPLAY_NAME },
null, null, null);
You actually need a RAW_CONTACT_ID here.
The DISPLAY_NAME can be fetched either from Contacts database/ContactsContract.Data' OR 'database/ContactsContract.CommonDataKinds.StructuredName' OR 'database/RawContactsEntity. In the later 2 cases you will be able to fetch the DISPLAY_NAME using RAW_CONTACT_ID
Couple of Key pointers:
Contacts._ID = Data.CONTACT_ID
RawContacts._ID = Data.RAW_CONTACT_ID
RawContacts.CONTACT_ID = Contacts._ID
RawContactsEntity._ID = RawContacts._ID
Sounds confusing?? Let me try...
The Contacts database is divided into 3 tables contacts, raw contacts, and data.
Each table contains column (_ID) which is an auto incremented primary key.
data table contains all the contact info like phone number, mail id, address etc.
The raw contacts points to the actual contact created. Hence we use the raw contacts while adding a contact.
The user cannot add any data in the contacts table. The data in this table is populated internally due to aggregation of contacts.
The reason your logic worked for some of the contacts is: _ID for contacts, raw contacts remains same until there is any contact aggregation taking place. Lets say you add two contacts with same name abc. Here the _ID for raw contacts increments twice while _ID for contacts increments only once as these two contacts gets merged due to the aggregation of contacts
Refer this for more details.
The best approach to fetch the info in your case is by using ContactsContract.RawContactsEntity ( an outer join of the raw_contacts table with the data table)
Reference: http://developer.android.com/reference/android/provider/ContactsContract.RawContactsEntity.html