I'm trying to make someones number a primary number in the specific contact numbers. This is the code:
Cursor the_phone = _context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.NUMBER +" = "+ numberToCall, null, null);
ContentValues values = new ContentValues();
if (the_phone.moveToFirst()){
values.put(ContactsContract.CommonDataKinds.Phone.LABEL,
the_phone.getString(the_phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL)));
values.put(ContactsContract.CommonDataKinds.Phone.IS_PRIMARY,1);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER,numberToCall);
int phones = _context.getContentResolver().update(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,values, ContactsContract.CommonDataKinds.Phone.NUMBER +" = "+ numberToCall, null);
}
Then I keep getting the following:
03-27 08:18:27.009: E/AndroidRuntime(640): FATAL EXCEPTION: main
03-27 08:18:27.009: E/AndroidRuntime(640): java.lang.UnsupportedOperationException: URI: content://com.android.contacts/data/phones, calling user: com...
I checked the first query and it's working I'm able to find the number in the resolver but unable to update it.
ok actually solved after looking into the resolver class.
private Boolean editPrimary( Cursor phones , String contactId, String contactNumber, int primaryTo){
ArrayList ops = new ArrayList();
String where = ContactsContract.Data.CONTACT_ID + " = ? AND " +
ContactsContract.CommonDataKinds.Phone.MIMETYPE + " = ? AND " +
String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE) + " = ? AND " +
ContactsContract.CommonDataKinds.Phone.NUMBER + " = ?";
String[] params = new String[] {contactId,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
String.valueOf(phones.getInt(phones.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.TYPE))),
contactNumber};
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(where, params)
.withValue(ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY,primaryTo)
.build());
try {
_context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
return true;
} catch (RemoteException e) {
e.printStackTrace();
return false;
} catch (OperationApplicationException e) {
e.printStackTrace();
return false;
}
}
from the developer guide we can see that :
Overview
ContactsContract defines an extensible database of contact-related information. Contact information is stored in a three-tier data model:
A row in the ContactsContract.Data table can store any kind of personal data, such as a phone number or email addresses. The set of data kinds that can be stored in this table is open-ended. There is a predefined set of common kinds, but any application can add its own data kinds.
A row in the ContactsContract.RawContacts table represents a set of data describing a person and associated with a single account (for example, one of the user's Gmail accounts).
A row in the ContactsContract.Contacts table represents an aggregate of one or more RawContacts presumably describing the same person. When data in or associated with the RawContacts table is changed, the affected aggregate contacts are updated as necessary.
so my first approach was wrong i didn't realize that the datakind class could represent the same data as the data class only directed to the context we are looking at
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 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).
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 trying to update the phone number associated with a contact on the SIM. I am able to update the Data table's row ok, but the change does not get propagated to the SIM. I dump out the Data row after it has been updated; the _ID is still the same, the phone (data1) col has the new phone number. When I powercycle the phone, the entry has the old value. The raw contact number of the contact has been incremented by 1. Any ideas on how to make the change persistent in the SIM? (Yes, I know some doc says access to SIM is not supported. But the Contacts app can do it - there must be a way for an app to update the SIM.) There may be typos - I had to (re)type the code by hand here.
Here is the code I use to update the row.
String rawId = "87"; // provided by caller; hardcoded here for brevity
String num = "212-222-3333"; // new phone #
ContentResolver cr = ...get actvity's resolver...
Uri uri = ContactsContract.Data.CONTENT_URI;
ContentValues cv = new ContentValues();
cv.put(ContactsContract.CommonDataKinds.Phone.Number, num);
String select = ContactsContract.Data.RAW_CONTACT_ID " + " = " + rawId + " and " +
ContactsContract.Data.MIMETYPE + " = '" +
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_ITEM + "'";
String[] selectArgs = {};
cr.update(uri, cv, select, selectArgs);
Catch exception and other details omitted.
Thanks for any clues!
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