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!
}
}
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
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 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
I'm still very new to android app development, and I have hit a problem I hope u can help me with...
I am trying to retrieve any "Notes" stored against a contact within my phone. I want to check if a contact (current caller) has any notes associated to them, and then either display the contents or do some action depending on the content of them etc...
I have tried the code below as a test to see if the data is retrieved anywhere within my cursor, but although it retrieves some data, I can't see the content of a note - so I guess I'm in the wrong place!
Where I have declared contentID, this is a result of doing a lookup with the code below, and using the id that is recieved.
Uri lookupUri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(inCommingNumber));
String[] mPhoneNumberProjection = { Phone._ID, Phone.NUMBER, Phone.DISPLAY_NAME};
Cursor cur = getContentResolver().query(lookupUri , mPhoneNumberProjection, null, null, null);
private boolean hasNote(String contactID){
Boolean noteFound = false;
Cursor noteCur = getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
if(noteCur.moveToFirst()) {
int numCols = noteCur.getColumnCount();
if(numCols > 0){
for(int x = 0; x < numCols; x++){
Log.d(APP_TAG, " column " + x + " contains: " + noteCur.getString(x));
}
}
noteFound = true;
} else{
Log.d(APP_TAG, "No Note retrieved");
}
noteCur.close();
return noteFound;
}
Apologies, I cant seem to get the code to display properly in this post!!
Any help would be great
I have tried various things, but can't seem to be able to get this data. I have created the note by simply adding it through the normal contact manager. I'm using Android 2.2.
The following code would display the first note for all contacts on your phone:
Cursor contactsCursor = null;
try {
contactsCursor = getContentResolver().query(RawContacts.CONTENT_URI,
new String [] { RawContacts._ID },
null, null, null);
if (contactsCursor != null && contactsCursor.moveToFirst()) {
do {
String rawContactId = contactsCursor.getString(0);
Cursor noteCursor = null;
try {
noteCursor = getContentResolver().query(Data.CONTENT_URI,
new String[] {Data._ID, Note.NOTE},
Data.RAW_CONTACT_ID + "=?" + " AND "
+ Data.MIMETYPE + "='" + Note.CONTENT_ITEM_TYPE + "'",
new String[] {rawContactId}, null);
if (noteCursor != null && noteCursor.moveToFirst()) {
String note = noteCursor.getString(noteCursor.getColumnIndex(Note.NOTE));
Log.d(APP_TAG, "Note: " + note);
}
} finally {
if (noteCursor != null) {
noteCursor.close();
}
}
} while (contactsCursor.moveToNext());
}
} finally {
if (contactsCursor != null) {
contactsCursor.close();
}
}
If you are storing some sort of structured data on the note to take actions on using the free form note field may not be the best approach. With the new contacts contract model you are able to attach your own data fields (in ContactsContract.Data) to a contact just give them your own unique mimetype.
I want to add a Custom field to the ContactsContract content provider. I'm trying to build a Voip application and would like to add a SIP address(name#domain) field to it. What MIME type will I need to associate with it?
Also I want to add a group address field which will have a list of group addresses in it (name#domain, name#domain,...). Which MIME type will I have to associate with this type of field.
I also want to add custom fields to the Call History like a session ID and SIP address(name#domain) field. How can I customize the call history?
It'll be great if someone can give me an example.
You have to creat your own mime type for those.
Here is an example that saves a boolean as my custom mime type to the contacts. It uses the latest SDK 2.1
public void saveFormality() {
try {
ContentValues values = new ContentValues();
values.put(Data.DATA1, this.getFormality() ? "1" : "0");
int mod = ctx.getContentResolver().update(
Data.CONTENT_URI,
values,
Data.CONTACT_ID + "=" + this.getId() + " AND "
+ Data.MIMETYPE + "= '"
+ clsContacts.FORMALITY_MIMETYPE + "'", null);
if (mod == 0) {
values.put(Data.CONTACT_ID, this.getId());
values.put(Data.MIMETYPE, clsContacts.FORMALITY_MIMETYPE);
ctx.getContentResolver().insert(Data.CONTENT_URI, values);
}
} catch (Exception e) {
Log.v(TAG(), "saveFormality failed");
}
}