5

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).

marmor
  • 27,641
  • 11
  • 107
  • 150

2 Answers2

8

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"});
Benoit Duffez
  • 11,839
  • 12
  • 77
  • 125
saschoar
  • 8,150
  • 5
  • 43
  • 46
  • Nice new api, thanks. Would this also modify the contacts-table to have all the contacts saved under the old account to be now under the new account? – marmor Feb 18 '15 at 11:32
  • I don't think that it's done automatically, because you basically create a new account which has no reference to the old one. The contacts provider has no idea what's going on. I added some code to my answer to update the values manually. Please see if that works. – saschoar Feb 18 '15 at 13:02
  • well, it's a very old question so I can't check the code, though it looks promising, i'll mark it as accepted for generations to come. – marmor Feb 18 '15 at 13:50
  • in my experience you need to update the db with the code provided by @saschoar before renaming the account, otherwise contacts are dropped. – Francesco Mapelli Jun 04 '15 at 09:56
  • Whenever I use `AccountManager.renameAccount()` it simply deletes the account! Google docs LIE! But the big questions: why am I surprised every time? – SMBiggs Jul 21 '22 at 15:05
0

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!
        }
    }
marmor
  • 27,641
  • 11
  • 107
  • 150
  • This doesn't seem to work... I'm still seeing the old name as the account both in Accounts & sync as well in the contact's data. – marmor Oct 25 '12 at 17:13
  • I'm marking this as the answer since this is as far as I went, the chances something bad will happen are greater then the benefit of changing that text on 'accounts & sync' so we're backing off – marmor Nov 14 '12 at 10:49
  • I guess this fails because you are renaming the `ACCOUNT_NAME` field in each of the contacts, but not the account itself. Contacts usually have nothing to do with the accounts managed by `AccountManager`. Please see my answer. – saschoar Feb 18 '15 at 10:22