I am working on the an application in which I want to update contact of particular person. When I update only contact first and last name then it working fine but I want to update full detail of contact like email address, number, postal addres etc.
Please provide me some useful link. Thanks in advance.
each field (email, name, adress) has its on mime type, which you should use
in order to update the field.
lets try to update the email for instance.
First, you should find the detail you want to update.
we will work with Data table, where each Data.RAW_CONTACT_ID represents a detail
about some contact.
So, we need to find the Data.RAW_CONTACT_ID where the id is the id of the contact you want
to edit.
Now we need to find the mimetype (the specific row which represents the detail) of
email (Email.CONTENT_ITEM_TYPE).
The data of an email is stored in the column Email.DATA - there we put the new email.
if you want a specific email type, you should add it to the query:
for example, if you want to add a home-email, then you should add Email.TYPE_HOME
to the query.
then we build a query and finally apply the change.
Here's an examle:
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
String emailParams = Data.RAW_CONTACT_ID + " = ? AND " + Data.MIMETYPE + " = ?";
String[] emailParamsWhere = new String[] { "contact_id", Email.CONTENT_ITEM_TYPE };
ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI).withSelection(emailParams, emailParamsWhere).withValue(Email.DATA, "new email").withValue(Email.TYPE, Email.TYPE_HOME)
.build());
try
{
ContentProviderResult[] res = getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
if (res != null)
{
return true;
}
return false;
}
catch (RemoteException e)
{
Log.d(TAG, e.getMessage());
e.printStackTrace();
}
catch (OperationApplicationException e)
{
Log.d(TAG, e.getMessage());
e.printStackTrace();
}
For updating mobile phone, use this query:
String phoneParams = Data.RAW_CONTACT_ID + " = ? AND " + Data.MIMETYPE + " = ? AND " + Phone.TYPE + " = " + Phone.TYPE_MOBILE;
String[] phoneParamsWhere = new String[] { "contact_id", Phone.CONTENT_ITEM_TYPE };
ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI).withSelection(phoneParams, phoneParamsWhere).withValue(Phone.NUMBER, "mobile_number")
.withValue(Phone.TYPE, Phone.TYPE_MOBILE).build());
Hope I helped
Related
Is it possible to retrieve all data from contact book by one request (phone number, email, first name, last name, photo, thumbnail). Right now I am running a separate request for a first and last name, a separate request for a phone number and a separate request for an email, and so on. Is it possible to get all this data in one request? Please help me.
Yes, you can.
You're probably running your queries on Alias tables such as CommonDataKinds.Phone and CommonDataKinds.Email, but the actual data for all these tables is stored in a single table called Data.
So you should query directly on Data and use the MIMETYPE column to figure out the "type" of the current row you're iterating over.
Here's an example of getting name, email, phone.
You can add more mimetypes to the list to get more types of data.
I'm using a HashMap to keep a list of values for each contact-ID but you would probably want to create a custom Contact class and put that info in it.
Map<Long, List<String>> contacts = new HashMap<Long, List<String>>();
String[] projection = {Data.CONTACT_ID, Data.MIMETYPE, Data.DATA1, Data.DATA2, Data.DATA3};
// query only name/emails/phones
String selection = Data.MIMETYPE + " IN ('" + StructuredName.CONTENT_ITEM_TYPE + "', '" + Phone.CONTENT_ITEM_TYPE + "', '" + Email.CONTENT_ITEM_TYPE + "')";
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
while (cur != null && cur.moveToNext()) {
long id = cur.getLong(0);
String mime = cur.getString(1); // type of data (name / phone / email)
String data = cur.getString(2); // the actual info, e.g. +1-212-555-1234
String kind = "unknown";
switch (mime) {
case Phone.CONTENT_ITEM_TYPE:
kind = "phone";
break;
case StructuredName.CONTENT_ITEM_TYPE:
kind = "name";
break;
case Email.CONTENT_ITEM_TYPE:
kind = "email";
break;
}
Log.d(TAG, "got " + id + ", " + kind + " - " + data);
// add info to existing list if this contact-id was already found, or create a new list in case it's new
List<String> infos;
if (contacts.containsKey(id)) {
infos = contacts.get(id);
} else {
infos = new ArrayList<String>();
contacts.put(id, infos);
}
infos.add(kind + " = " + data);
}
I am having issue where i need to have update Multiple Mobile no's , Landline no's and Emails , websites , address in local phonebook contact.
If contact is already exist in phone book then i am trying to update it's details.
Below is the code i am trying. It is getting executed without error but no multiple mobile no , landline , email etc not reflecting in phone-book on that contact as the multiple data's are their.
I also referred few of the links but that didn't helped as well.
String whereMobile = ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " +
ContactsContract.Data.MIMETYPE + " = ? AND " +
ContactsContract.CommonDataKinds.Phone.TYPE + " = ? ";
String[] paramsMobile = new String[]{String.valueOf(contactID),
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)};
if (userInfoMobileNos != null && !userInfoMobileNos.isEmpty()) {
for (int iUserMobile = 0; iUserMobile < userInfoMobileNos.size(); iUserMobile++) {
operationList.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(whereMobile, paramsMobile)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, userInfoMobileNos.get(iUserMobile).getPhoneNumber())
.build());
}
}
String whereGmail = ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " +
ContactsContract.Data.MIMETYPE + " = ? AND " +
ContactsContract.CommonDataKinds.Email.TYPE + " = ? ";
String[] paramsGmail = new String[]{String.valueOf(contactID),
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
String.valueOf(ContactsContract.CommonDataKinds.Email.TYPE_WORK)};
if (userInfoGmails != null && !userInfoGmails.isEmpty()) {
for (int iUserGmail = 0; iUserGmail < userInfoGmails.size(); iUserGmail++) {
operationList.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(whereGmail, paramsGmail)
.withValue(ContactsContract.CommonDataKinds.Email.DATA, userInfoGmails.get(iUserGmail).getEmail())
.build());
}
}
Updated code
private void updateNew(Context context, int rawContactID) {
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
String whereMobile = ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " +
ContactsContract.Data.MIMETYPE + " = ? AND " +
ContactsContract.CommonDataKinds.Phone.TYPE + " = ? ";
String[] paramsMobile = new String[]{String.valueOf(rawContactID),
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)};
// First delete all the existing phones with type mobile, if any exist
operationList.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(whereMobile, paramsMobile)
.build());
// Next, insert all the updated phones:
for (MobileNumbersItem infoMobileNo : userInfoMobileNos) { // <== change the class to the one you're using in userInfoMobileNos
operationList.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) // <== insert not update!
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, infoMobileNo.getPhoneNumber())
.build());
}
String whereGmail = ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " +
ContactsContract.Data.MIMETYPE + " = ? AND " +
ContactsContract.CommonDataKinds.Email.TYPE + " = ? ";
String[] paramsGmail = new String[]{String.valueOf(rawContactID),
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
String.valueOf(ContactsContract.CommonDataKinds.Email.TYPE_WORK)};
// First delete all the existing emails with type work, if any exist
operationList.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(whereGmail, paramsGmail)
.build());
// Next, insert all the updated emails:
for (GmailsItem userInfoGmail : userInfoGmails) { // <== change the class here too
operationList.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK)
.withValue(ContactsContract.CommonDataKinds.Email.DATA, userInfoGmail.getEmail())
.build());
}
try {
// don't forget to apply the operations now:
ContentProviderResult[] results = context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
Log.d("UPDATE CONTACRT", "results=" + Arrays.toString(results));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("updateContactError", Objects.requireNonNull(e.getMessage()));
}
}
Any help will be appreciated here. I have tried to debug but can't able to find the issue.
one issue that can be either a bad parameter name, or an actual bug is that your selection is requesting a RAW_CONTACT_ID and supplying a parameter called contactId, I'm not sure what is stored in contactId but there's a big difference between a rawContactId and a contactId, so if that's indeed a contactId your operations will not find anything to update (or even worse, update the wrong contact).
The second issue is that your two loops (over userInfoMobileNos and userInfoGmails) keep updating (overriding) the same Data row, so eventually you should end up with just the last value in the contact details.
You're also not checking if there's a Data row in the contact details that fit your selection, if there's not, there will be no row to update, and therefore the code will simply do nothing.
Third issue, but it may be intentional, not sure how you expect your app to work, is that you assume the info item type, i.e. you only update a phone if it's TYPE_MOBILE, and you only update the email if it's TYPE_WORK, what if there are other phones and emails stored for that contact? your code will simply keep those untouched, not sure if that intentional or not.
To fix issue one, just make sure the value in contactId is a RawContactId and change the name of the param.
For the second issue, you should instead do a delete-and-insert, see code below.
For the third issue, you'll need to change the selection code (paramsMobile) to remove it's assumption of label.
Here's suggested code:
String whereMobile = Data.RAW_CONTACT_ID + " = ? AND " +
Data.MIMETYPE + " = ? AND " +
Phone.TYPE + " = ? ";
String[] paramsMobile = new String[]{String.valueOf(rawContactID),
Phone.CONTENT_ITEM_TYPE,
String.valueOf(Phone.TYPE_MOBILE)};
// First delete all the existing phones with type mobile, if any exist
operationList.add(ContentProviderOperation.newDelete(Data.CONTENT_URI)
.withSelection(whereMobile, paramsMobile)
.build());
// Next, insert all the updated phones:
for (UserInfo infoMobileNo : userInfoMobileNos) { // <== change the class to the one you're using in userInfoMobileNos
operationList.add(ContentProviderOperation.newInsert(Data.CONTENT_URI) // <== insert not update!
operationList.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) // <== insert not update!
.withValue(Data.RAW_CONTACT_ID, rawContactID)
.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.TYPE, Phone.TYPE_MOBILE)
.withValue(Phone.NUMBER, infoMobileNo.getPhoneNumber())
.build());
}
String whereGmail = Data.RAW_CONTACT_ID + " = ? AND " +
Data.MIMETYPE + " = ? AND " +
Email.TYPE + " = ? ";
String[] paramsGmail = new String[]{String.valueOf(rawContactID),
Email.CONTENT_ITEM_TYPE,
String.valueOf(Email.TYPE_WORK)};
// First delete all the existing emails with type work, if any exist
operationList.add(ContentProviderOperation.newDelete(Data.CONTENT_URI)
.withSelection(whereGmail, paramsGmail)
.build());
// Next, insert all the updated emails:
for (UserInfo userInfoGmail : userInfoGmails) { // <== change the class here too
operationList.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
.withValue(Data.RAW_CONTACT_ID, rawContactID)
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.TYPE, Email.TYPE_WORK)
.withValue(Email.DATA, userInfoGmail.getEmail())
.build());
}
// don't forget to apply the operations now:
ContentProviderResult[] results = context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
Log.d("UPDATE CONTACRT", "results=" + Arrays.toString(results));
Am I the only one trying to achieve this ... :/ ?
In short:
I want to fetch back the picture of my contacts as they do defined it by themselves (on their Google own Account page for instance).
Use case: I have modified one of my contact's picture myself, and now, I want to undo that change -> I want to 'fetch back' the Google picture of my contact (the one set by him/herself).
I have an app that manage Google Contacts. It also manage contact photo using
ContactsContract.CommonDataKinds.Photo.PHOTO
And it's working fine.
Here is a scenario I would like to support:
I add a new contact in my contact list entering it's gmail address. (OK)
After a while, contact photo is available on my contact app (since contact has a picture on its Google account, AND contact sync is ON on the Android device). (OK)
Within my app, I change the app contact picture (so, I 'override' contact picture) (OK)
Within my app, I want to get back default Google contact picture: Not OK. How can I achieve that?
Please take a look at my code here to set the Photo.
Should I just 'clear' the photo and relies on ContactProvider to download back user photo from Google account?
How can I clear the photo. Set ContactsContract.CommonDataKinds.Photo.PHOTO to 'null'? and delete the associated file, i.e.,
Uri rawContactPhotoUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), RawContacts.DisplayPhoto.CONTENT_DIRECTORY)
Thanks for helping.
Here is how I update picture:
private void updatePhotoThumbnail(Bitmap bitmap, Contact contact) throws Exception
{
byte[] contactPhotoBytes = getContactPhotoBytes(bitmap);
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
// #formatter:off
String where = ContactsContract.RawContacts.ACCOUNT_NAME + "= ? "
+ "AND " + ContactsContract.RawContacts.ACCOUNT_TYPE + "= ? "
+ "AND " + ContactsContract.Data.CONTACT_ID + "= ? "
+ "AND " + ContactsContract.Data.RAW_CONTACT_ID + "= ? "
+ "AND " + ContactsContract.Data.MIMETYPE + " = ?";
// #formatter:on
String[] params = new String[]
{
// #formatter:off
_accountName,
AccountManagerHelper.GOOGLE_ACCOUNT_TYPE,
String.valueOf(contact.getId()),
String.valueOf(contact.getRawContactId()),
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
// #formatter:on
};
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).withSelection(where, params)
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, contactPhotoBytes).build());
try
{
_contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
updateDisplayPhoto(contact.getRawContactId(), contactPhotoBytes);
}
catch (RemoteException e)
{
e.printStackTrace();
throw new Exception(e.getMessage());
}
catch (OperationApplicationException e)
{
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
private void updateDisplayPhoto(long rawContactId, byte[] photo)
{
Uri rawContactPhotoUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
try
{
AssetFileDescriptor fd = getContentResolver().openAssetFileDescriptor(rawContactPhotoUri, "rw");
OutputStream os = fd.createOutputStream();
os.write(photo);
os.close();
fd.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
Here is a tutorial to retrieve user's Google profile picture. It also retrieves some other things like account Email, name, ...
I think this is the straight way for your question :)
Stay in contact if it hepled.
I'm changing the name of a published app.
Is there a quick and safe way to change the account name created via AccountManager.addAccountExplicitly so that existing info will remain intact for existing users.
If not, how can I go about changing the account name manually while preserving all the data?
I'll post an answer of my naive approach of copying everything then deleting the old, but I'm sure someone will come up with a better one (or spot some bugs in my method).
API v21 added a renameAccount() method to the AccountManager, if that helps.
From the docs:
This is equivalent to removing the existing account and adding a new
renamed account with the old account's user data.
That means for backward compatibility, you would have to manually remove the account and run through the same procedure as creating a new one (AccountManager.addAccountExplicitly() and AccountManager.setUserData()) afterwards.
Edit:
If you want to update your contacts afterwards to display the correct account name, try this (untested) code:
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.RawContacts.ACCOUNT_NAME, "new account name");
getContext().getContentResolver().update(ContactsContract.RawContacts.CONTENT_URI,
contentValues,
ContactsContract.RawContacts.ACCOUNT_TYPE + " = ? AND " + ContactsContract.RawContacts.ACCOUNT_NAME + " = ?",
new String[]{"your account type", "old account name"});
A naive approach of going over all the records, copying them one by one, and deleting all the old stuff...
I'm really afraid this method might fail on real world users.
private void naiveRename(ContentResolver resolver) {
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
Cursor cur = resolver.query(RawContacts.CONTENT_URI, null, RawContacts.ACCOUNT_NAME + "='"
+ "OLD NAME" + "'", null, null);
if (cur != null) {
// copy all data
while (cur.moveToNext()) {
Uri curUri = RawContacts.CONTENT_URI.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build();
ContentProviderOperation.Builder builder = ContentProviderOperation
.newInsert(curUri);
for (int i = 0; i < cur.getColumnCount(); i++) {
String colName = cur.getColumnName(i);
if (RawContacts._ID.equals(colName) || RawContacts.VERSION.equals(colName)
|| RawContacts.CONTACT_ID.equals(colName)) {
// Skip - read only
} else if (RawContacts.ACCOUNT_NAME.equals(colName)) {
builder.withValue(RawContacts.ACCOUNT_NAME, "NEW NAME");
} else {
builder.withValue(colName, cur.getString(i));
}
}
operationList.add(builder.build());
}
// delete all old data
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(RawContacts.CONTENT_URI);
builder.withSelection(RawContacts.ACCOUNT_NAME + "='" + "OLD NAME" + "'", null);
try {
resolver.applyBatch(ContactsContract.AUTHORITY, operationList);
} catch (RemoteException e) {
// PANIC!
} catch (OperationApplicationException e) {
// OMG! WHAT TO DO?!
}
} else {
// LORDI!
}
}
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