Quick backup/restore of accounts
* Workaround for (HTC bug 2275383) & (Moto bug 2226582) * Adds checkpoints for backing up and restoring accounts * Uses legacy Account / prefs to back up accounts - this is because some of this code will be reused for legacy account migration * Unit tests of Account & LegacyConversions * Unit tests of backup & restore * Not done: testing of EAS/Account Manager interface (this will require deeper dependency injection, to avoid the embedded calls to the Account Manager and other system services.)
This commit is contained in:
parent
950f6c65dd
commit
5e91cccd4b
|
@ -22,7 +22,6 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -30,7 +29,7 @@ import java.util.UUID;
|
|||
* Account stores all of the settings for a single account defined by the user. It is able to save
|
||||
* and delete itself given a Preferences to work with. Each account is defined by a UUID.
|
||||
*/
|
||||
public class Account implements Serializable {
|
||||
public class Account {
|
||||
public static final int DELETE_POLICY_NEVER = 0;
|
||||
public static final int DELETE_POLICY_7DAYS = 1;
|
||||
public static final int DELETE_POLICY_ON_DELETE = 2;
|
||||
|
@ -46,12 +45,11 @@ public class Account implements Serializable {
|
|||
public static final int SYNC_WINDOW_1_MONTH = 5;
|
||||
public static final int SYNC_WINDOW_ALL = 6;
|
||||
|
||||
/**
|
||||
* This should never be used for persistance, only for marshalling.
|
||||
* TODO: Remove serializable (VERY SLOW) and replace with Parcelable
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// These flags will never be seen in a "real" (legacy) account
|
||||
public static final int BACKUP_FLAGS_IS_BACKUP = 1;
|
||||
public static final int BACKUP_FLAGS_SYNC_CONTACTS = 2;
|
||||
public static final int BACKUP_FLAGS_IS_DEFAULT = 4;
|
||||
|
||||
// transient values - do not serialize
|
||||
private transient Preferences mPreferences;
|
||||
|
||||
|
@ -74,6 +72,8 @@ public class Account implements Serializable {
|
|||
boolean mVibrate;
|
||||
String mRingtoneUri;
|
||||
int mSyncWindow;
|
||||
int mBackupFlags; // for account backups only
|
||||
String mProtocolVersion; // for account backups only
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
|
@ -88,6 +88,8 @@ public class Account implements Serializable {
|
|||
* All new fields should have named keys
|
||||
*/
|
||||
private final String KEY_SYNC_WINDOW = ".syncWindow";
|
||||
private final String KEY_BACKUP_FLAGS = ".backupFlags";
|
||||
private final String KEY_PROTOCOL_VERSION = ".protocolVersion";
|
||||
|
||||
public Account(Context context) {
|
||||
// TODO Change local store path to something readable / recognizable
|
||||
|
@ -99,6 +101,8 @@ public class Account implements Serializable {
|
|||
mVibrate = false;
|
||||
mRingtoneUri = "content://settings/system/notification_sound";
|
||||
mSyncWindow = SYNC_WINDOW_USER; // IMAP & POP3
|
||||
mBackupFlags = 0;
|
||||
mProtocolVersion = null;
|
||||
}
|
||||
|
||||
Account(Preferences preferences, String uuid) {
|
||||
|
@ -157,6 +161,10 @@ public class Account implements Serializable {
|
|||
|
||||
mSyncWindow = preferences.mSharedPreferences.getInt(mUuid + KEY_SYNC_WINDOW,
|
||||
SYNC_WINDOW_USER);
|
||||
|
||||
mBackupFlags = preferences.mSharedPreferences.getInt(mUuid + KEY_BACKUP_FLAGS, 0);
|
||||
mProtocolVersion = preferences.mSharedPreferences.getString(mUuid + KEY_PROTOCOL_VERSION,
|
||||
null);
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
|
@ -252,6 +260,8 @@ public class Account implements Serializable {
|
|||
editor.remove(mUuid + ".vibrate");
|
||||
editor.remove(mUuid + ".ringtone");
|
||||
editor.remove(mUuid + KEY_SYNC_WINDOW);
|
||||
editor.remove(mUuid + KEY_BACKUP_FLAGS);
|
||||
editor.remove(mUuid + KEY_PROTOCOL_VERSION);
|
||||
|
||||
// also delete any deprecated fields
|
||||
editor.remove(mUuid + ".transportUri");
|
||||
|
@ -315,6 +325,8 @@ public class Account implements Serializable {
|
|||
editor.putBoolean(mUuid + ".vibrate", mVibrate);
|
||||
editor.putString(mUuid + ".ringtone", mRingtoneUri);
|
||||
editor.putInt(mUuid + KEY_SYNC_WINDOW, mSyncWindow);
|
||||
editor.putInt(mUuid + KEY_BACKUP_FLAGS, mBackupFlags);
|
||||
editor.putString(mUuid + KEY_PROTOCOL_VERSION, mProtocolVersion);
|
||||
|
||||
// The following fields are *not* written because they need to be more fine-grained
|
||||
// and not risk rewriting with old data.
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email;
|
||||
|
||||
import com.android.email.mail.store.ExchangeStore;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.exchange.Eas;
|
||||
|
||||
import android.accounts.AccountManagerFuture;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Utility functions to support backup and restore of accounts.
|
||||
*
|
||||
* In the short term, this is used to work around local database failures. In the long term,
|
||||
* this will also support server-side backups, providing support for automatic account restoration
|
||||
* when switching or replacing phones.
|
||||
*/
|
||||
public class AccountBackupRestore {
|
||||
|
||||
/**
|
||||
* Backup accounts. Can be called from UI thread (does work in a new thread)
|
||||
*/
|
||||
public static void backupAccounts(final Context context) {
|
||||
if (Email.DEBUG) {
|
||||
Log.v(Email.LOG_TAG, "backupAccounts");
|
||||
}
|
||||
// Because we typically call this from the UI, let's do the work in a thread
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(AccountBackupRestore.class) {
|
||||
doBackupAccounts(context, Preferences.getPreferences(context));
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore accounts if needed. This is blocking, and should only be called in specific
|
||||
* startup/entry points.
|
||||
*/
|
||||
public static void restoreAccountsIfNeeded(final Context context) {
|
||||
// Don't log here; This is called often.
|
||||
boolean restored;
|
||||
synchronized(AccountBackupRestore.class) {
|
||||
restored = doRestoreAccounts(context, Preferences.getPreferences(context));
|
||||
}
|
||||
if (restored) {
|
||||
// after restoring accounts, register services appropriately
|
||||
Log.w(Email.LOG_TAG, "Register services after restoring accounts");
|
||||
Email.setServicesEnabled(context);
|
||||
context.startService(new Intent(context.getApplicationContext(),
|
||||
com.android.exchange.SyncManager.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-UI-Thread worker to backup all accounts
|
||||
*
|
||||
* @param context used to access the provider
|
||||
* @param preferences used to access the backups (provided separately for testability)
|
||||
*/
|
||||
/* package */ static void doBackupAccounts(Context context, Preferences preferences) {
|
||||
// 1. Wipe any existing backup accounts
|
||||
Account[] oldBackups = preferences.getAccounts();
|
||||
for (Account backup : oldBackups) {
|
||||
backup.delete(preferences);
|
||||
}
|
||||
|
||||
// 2. Identify the default account (if any). This is required because setting
|
||||
// the default account flag is lazy,and sometimes we don't have any flags set. We'll
|
||||
// use this to make it explicit (see loop, below).
|
||||
long defaultAccountId = EmailContent.Account.getDefaultAccountId(context);
|
||||
if (defaultAccountId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Create new backup(s), if any
|
||||
Cursor c = context.getContentResolver().query(EmailContent.Account.CONTENT_URI,
|
||||
EmailContent.Account.CONTENT_PROJECTION, null, null, null);
|
||||
try {
|
||||
while (c.moveToNext()) {
|
||||
EmailContent.Account fromAccount =
|
||||
EmailContent.getContent(c, EmailContent.Account.class);
|
||||
if (Email.DEBUG) {
|
||||
Log.v(Email.LOG_TAG, "Backing up account:" + fromAccount.getDisplayName());
|
||||
}
|
||||
Account toAccount = LegacyConversions.makeLegacyAccount(context, fromAccount);
|
||||
|
||||
// Determine if contacts are also synced, and if so, record that
|
||||
if (fromAccount.mHostAuthRecv.mProtocol.equals("eas")) {
|
||||
android.accounts.Account acct = new android.accounts.Account(
|
||||
fromAccount.mEmailAddress, Eas.ACCOUNT_MANAGER_TYPE);
|
||||
boolean syncContacts = ContentResolver.getSyncAutomatically(acct,
|
||||
ContactsContract.AUTHORITY);
|
||||
if (syncContacts) {
|
||||
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_SYNC_CONTACTS;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the default account, mark it as such
|
||||
if (fromAccount.mId == defaultAccountId) {
|
||||
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_IS_DEFAULT;
|
||||
}
|
||||
|
||||
// Mark this account as a backup of a Provider account, instead of a legacy
|
||||
// account to upgrade
|
||||
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_IS_BACKUP;
|
||||
|
||||
toAccount.save(preferences);
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore all accounts. This is blocking.
|
||||
*
|
||||
* @param context used to access the provider
|
||||
* @param preferences used to access the backups (provided separately for testability)
|
||||
* @return true if accounts were restored (meaning services should be restarted, etc.)
|
||||
*/
|
||||
/* package */ static boolean doRestoreAccounts(Context context, Preferences preferences) {
|
||||
boolean result = false;
|
||||
|
||||
// 1. Quick check - if we have any accounts, get out
|
||||
int numAccounts = EmailContent.count(context, EmailContent.Account.CONTENT_URI, null, null);
|
||||
if (numAccounts > 0) {
|
||||
return result;
|
||||
}
|
||||
// 2. Quick check - if no backup accounts, get out
|
||||
Account[] backups = preferences.getAccounts();
|
||||
if (backups.length == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Log.w(Email.LOG_TAG, "*** Restoring Email Accounts, found " + backups.length);
|
||||
|
||||
// 3. Possible lost accounts situation - check for any backups, and restore them
|
||||
for (Account backupAccount : backups) {
|
||||
// don't back up any leftover legacy accounts (these are migrated elsewhere).
|
||||
if ((backupAccount.mBackupFlags & Account.BACKUP_FLAGS_IS_BACKUP) == 0) {
|
||||
continue;
|
||||
}
|
||||
// Restore the account
|
||||
Log.v(Email.LOG_TAG, "Restoring account:" + backupAccount.getDescription());
|
||||
EmailContent.Account toAccount =
|
||||
LegacyConversions.makeAccount(context, backupAccount);
|
||||
|
||||
// Mark the default account if this is it
|
||||
if (0 != (backupAccount.mBackupFlags & Account.BACKUP_FLAGS_IS_DEFAULT)) {
|
||||
toAccount.setDefaultAccount(true);
|
||||
}
|
||||
|
||||
// For exchange accounts, handle system account first, then save in provider
|
||||
if (toAccount.mHostAuthRecv.mProtocol.equals("eas")) {
|
||||
// Recreate entry in Account Manager as well, if needed
|
||||
// Set "sync contacts" mode as well, if needed
|
||||
boolean alsoSyncContacts =
|
||||
(backupAccount.mBackupFlags & Account.BACKUP_FLAGS_SYNC_CONTACTS) != 0;
|
||||
|
||||
// Use delete-then-add semantic to simplify handling of update-in-place
|
||||
// AccountManagerFuture<Boolean> removeResult = ExchangeStore.removeSystemAccount(
|
||||
// context.getApplicationContext(), toAccount, null);
|
||||
// try {
|
||||
// // This call blocks until removeSystemAccount completes. Result is not used.
|
||||
// removeResult.getResult();
|
||||
// } catch (AccountsException e) {
|
||||
// Log.d(Email.LOG_TAG, "removeSystemAccount failed: " + e);
|
||||
// // log and discard - we don't care if remove fails, generally
|
||||
// } catch (IOException e) {
|
||||
// Log.d(Email.LOG_TAG, "removeSystemAccount failed: " + e);
|
||||
// // log and discard - we don't care if remove fails, generally
|
||||
// }
|
||||
|
||||
// NOTE: We must use the Application here, rather than the current context, because
|
||||
// all future references to AccountManager will use the context passed in here
|
||||
// TODO: Need to implement overwrite semantics for an already-installed account
|
||||
AccountManagerFuture<Bundle> addAccountResult = ExchangeStore.addSystemAccount(
|
||||
context.getApplicationContext(), toAccount, alsoSyncContacts, null);
|
||||
// try {
|
||||
// // This call blocks until addSystemAccount completes. Result is not used.
|
||||
// addAccountResult.getResult();
|
||||
toAccount.save(context);
|
||||
// } catch (OperationCanceledException e) {
|
||||
// Log.d(Email.LOG_TAG, "addAccount was canceled");
|
||||
// } catch (IOException e) {
|
||||
// Log.d(Email.LOG_TAG, "addAccount failed: " + e);
|
||||
// } catch (AuthenticatorException e) {
|
||||
// Log.d(Email.LOG_TAG, "addAccount failed: " + e);
|
||||
// }
|
||||
|
||||
} else {
|
||||
// non-eas account - save it immediately
|
||||
toAccount.save(context);
|
||||
}
|
||||
// report that an account was restored
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -521,4 +521,84 @@ public class LegacyConversions {
|
|||
}
|
||||
mp.addBodyPart(bp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion from provider account to legacy account
|
||||
*
|
||||
* Used for backup/restore.
|
||||
*
|
||||
* @param context application context
|
||||
* @param fromAccount the provider account to be backed up (including transient hostauth's)
|
||||
* @result a legacy Account object ready to be committed to preferences
|
||||
*/
|
||||
/* package */ static Account makeLegacyAccount(Context context,
|
||||
EmailContent.Account fromAccount) {
|
||||
Account result = new Account(context);
|
||||
|
||||
result.setDescription(fromAccount.getDisplayName());
|
||||
result.setEmail(fromAccount.getEmailAddress());
|
||||
// fromAccount.mSyncKey - assume lost if restoring
|
||||
result.setSyncWindow(fromAccount.getSyncLookback());
|
||||
result.setAutomaticCheckIntervalMinutes(fromAccount.getSyncInterval());
|
||||
// fromAccount.mHostAuthKeyRecv - id not saved; will be reassigned when restoring
|
||||
// fromAccount.mHostAuthKeySend - id not saved; will be reassigned when restoring
|
||||
|
||||
// Provider Account flags, and how they are mapped.
|
||||
// FLAGS_NOTIFY_NEW_MAIL -> mNotifyNewMail
|
||||
// FLAGS_VIBRATE -> mVibrate
|
||||
// DELETE_POLICY_NEVER -> mDeletePolicy
|
||||
// DELETE_POLICY_7DAYS
|
||||
// DELETE_POLICY_ON_DELETE
|
||||
result.setNotifyNewMail(0 !=
|
||||
(fromAccount.getFlags() & EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL));
|
||||
result.setVibrate(0 !=
|
||||
(fromAccount.getFlags() & EmailContent.Account.FLAGS_VIBRATE));
|
||||
result.setDeletePolicy(fromAccount.getDeletePolicy());
|
||||
|
||||
result.mUuid = fromAccount.getUuid();
|
||||
result.setName(fromAccount.mSenderName);
|
||||
result.setRingtone(fromAccount.mRingtoneUri);
|
||||
result.mProtocolVersion = fromAccount.mProtocolVersion;
|
||||
// int fromAccount.mNewMessageCount = will be reset on next sync
|
||||
|
||||
// Use the existing conversions from HostAuth <-> Uri
|
||||
result.setStoreUri(fromAccount.getStoreUri(context));
|
||||
result.setSenderUri(fromAccount.getSenderUri(context));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion from legacy account to provider account
|
||||
*
|
||||
* Used for backup/restore and for account migration.
|
||||
*/
|
||||
/* package */ static EmailContent.Account makeAccount(Context context, Account fromAccount) {
|
||||
|
||||
EmailContent.Account result = new EmailContent.Account();
|
||||
|
||||
result.setDisplayName(fromAccount.getDescription());
|
||||
result.setEmailAddress(fromAccount.getEmail());
|
||||
result.mSyncKey = "";
|
||||
result.setSyncLookback(fromAccount.getSyncWindow());
|
||||
result.setSyncInterval(fromAccount.getAutomaticCheckIntervalMinutes());
|
||||
// result.mHostAuthKeyRecv
|
||||
// result.mHostAuthKeySend;
|
||||
int flags = 0;
|
||||
if (fromAccount.isNotifyNewMail()) flags |= EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL;
|
||||
if (fromAccount.isVibrate()) flags |= EmailContent.Account.FLAGS_VIBRATE;
|
||||
result.setFlags(flags);
|
||||
result.setDeletePolicy(fromAccount.getDeletePolicy());
|
||||
// result.setDefaultAccount();
|
||||
result.mCompatibilityUuid = fromAccount.getUuid();
|
||||
result.setSenderName(fromAccount.getName());
|
||||
result.setRingtone(fromAccount.getRingtone());
|
||||
result.mProtocolVersion = fromAccount.mProtocolVersion;
|
||||
result.mNewMessageCount = 0;
|
||||
|
||||
result.setStoreUri(context, fromAccount.getStoreUri());
|
||||
result.setSenderUri(context, fromAccount.getSenderUri());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.activity;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.Controller;
|
||||
import com.android.email.Email;
|
||||
import com.android.email.R;
|
||||
|
@ -447,6 +448,8 @@ public class AccountFolderList extends ListActivity implements OnItemClickListen
|
|||
Uri uri = ContentUris.withAppendedId(
|
||||
EmailContent.Account.CONTENT_URI, mSelectedContextAccount.mId);
|
||||
AccountFolderList.this.getContentResolver().delete(uri, null, null);
|
||||
// Update the backup (side copy) of the accounts
|
||||
AccountBackupRestore.backupAccounts(AccountFolderList.this);
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.activity;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.activity.setup.AccountSetupBasics;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.Mailbox;
|
||||
|
@ -47,6 +48,13 @@ public class Welcome extends Activity {
|
|||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
// Restore accounts, if it has not happened already
|
||||
// NOTE: This is blocking, which it should not be (in the UI thread)
|
||||
// We're going to live with this for the short term and replace with something
|
||||
// smarter. Long-term fix: Move this, and most of the code below, to an AsyncTask
|
||||
// and do the DB work in a thread. Then post handler to finish() as appropriate.
|
||||
AccountBackupRestore.restoreAccountsIfNeeded(this);
|
||||
|
||||
// Because the app could be reloaded (for debugging, etc.), we need to make sure that
|
||||
// SyncManager gets a chance to start. There is no harm to starting it if it has already
|
||||
// been started
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.activity.setup;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.AccountColumns;
|
||||
|
||||
|
@ -45,5 +46,7 @@ public class AccountSettingsUtils {
|
|||
cv.put(AccountColumns.SYNC_LOOKBACK, account.mSyncLookback);
|
||||
account.update(context, cv);
|
||||
}
|
||||
// Update the backup (side copy) of the accounts
|
||||
AccountBackupRestore.backupAccounts(context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.activity.setup;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.Email;
|
||||
import com.android.email.EmailAddressValidator;
|
||||
import com.android.email.R;
|
||||
|
@ -444,6 +445,8 @@ public class AccountSetupBasics extends Activity
|
|||
// At this point we write the Account object to the DB for the first time.
|
||||
// From now on we'll only pass the accountId around.
|
||||
mAccount.save(this);
|
||||
// Update the backup (side copy) of the accounts
|
||||
AccountBackupRestore.backupAccounts(this);
|
||||
Email.setServicesEnabled(this);
|
||||
AccountSetupNames.actionSetNames(this, mAccount.mId, false);
|
||||
finish();
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.activity.setup;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.R;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.provider.EmailContent;
|
||||
|
@ -310,6 +311,8 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
|||
// Account.save will save the HostAuth's
|
||||
mAccount.save(this);
|
||||
}
|
||||
// Update the backup (side copy) of the accounts
|
||||
AccountBackupRestore.backupAccounts(this);
|
||||
finish();
|
||||
} else {
|
||||
// Go directly to end - there is no 2nd screen for incoming settings
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package com.android.email.activity.setup;
|
||||
|
||||
import com.android.email.Account;
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.R;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.provider.EmailContent;
|
||||
|
@ -334,6 +335,8 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
|
|||
} else {
|
||||
mAccount.save(this);
|
||||
}
|
||||
// Update the backup (side copy) of the accounts
|
||||
AccountBackupRestore.backupAccounts(this);
|
||||
finish();
|
||||
} else {
|
||||
/*
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.activity.setup;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.R;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.provider.EmailContent;
|
||||
|
@ -127,6 +128,8 @@ public class AccountSetupNames extends Activity implements OnClickListener {
|
|||
cv.put(AccountColumns.DISPLAY_NAME, mAccount.getDisplayName());
|
||||
cv.put(AccountColumns.SENDER_NAME, name);
|
||||
mAccount.update(this, cv);
|
||||
// Update the backup (side copy) of the accounts
|
||||
AccountBackupRestore.backupAccounts(this);
|
||||
onBackPressed();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.activity.setup;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.R;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.provider.EmailContent;
|
||||
|
@ -255,6 +256,8 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener,
|
|||
} else {
|
||||
mAccount.save(this);
|
||||
}
|
||||
// Update the backup (side copy) of the accounts
|
||||
AccountBackupRestore.backupAccounts(this);
|
||||
finish();
|
||||
} else {
|
||||
AccountSetupOptions.actionOptions(this, mAccount, mMakeDefault, false);
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.android.exchange.SyncManager;
|
|||
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AccountManagerCallback;
|
||||
import android.accounts.AccountManagerFuture;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
|
@ -79,8 +80,8 @@ public class ExchangeStore extends Store {
|
|||
mTransport.checkSettings(mUri);
|
||||
}
|
||||
|
||||
static public void addSystemAccount(Context context, Account acct, boolean syncContacts,
|
||||
AccountManagerCallback<Bundle> callback) {
|
||||
static public AccountManagerFuture<Bundle> addSystemAccount(Context context, Account acct,
|
||||
boolean syncContacts, AccountManagerCallback<Bundle> callback) {
|
||||
// Create a description of the new account
|
||||
Bundle options = new Bundle();
|
||||
options.putString(EasAuthenticatorService.OPTIONS_USERNAME, acct.mEmailAddress);
|
||||
|
@ -90,10 +91,25 @@ public class ExchangeStore extends Store {
|
|||
// Here's where we tell AccountManager about the new account. The addAccount
|
||||
// method in AccountManager calls the addAccount method in our authenticator
|
||||
// service (EasAuthenticatorService)
|
||||
AccountManager.get(context).addAccount(Eas.ACCOUNT_MANAGER_TYPE, null, null,
|
||||
return AccountManager.get(context).addAccount(Eas.ACCOUNT_MANAGER_TYPE, null, null,
|
||||
options, null, callback, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an account from the Account manager - see {@link AccountManager#removeAccount(
|
||||
* android.accounts.Account, AccountManagerCallback, android.os.Handler)}.
|
||||
*
|
||||
* @param context context to use
|
||||
* @param acct the account to remove
|
||||
* @param callback async results callback - pass null to use blocking mode
|
||||
*/
|
||||
static public AccountManagerFuture<Boolean> removeSystemAccount(Context context, Account acct,
|
||||
AccountManagerCallback<Bundle> callback) {
|
||||
android.accounts.Account systemAccount =
|
||||
new android.accounts.Account(acct.mEmailAddress, Eas.ACCOUNT_MANAGER_TYPE);
|
||||
return AccountManager.get(context).removeAccount(systemAccount, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Folder getFolder(String name) {
|
||||
return null;
|
||||
|
|
|
@ -1067,7 +1067,7 @@ public abstract class EmailContent {
|
|||
* with accounts set up by previous versions, because there are externals references
|
||||
* to the Uuid (e.g. desktop shortcuts).
|
||||
*/
|
||||
String getUuid() {
|
||||
public String getUuid() {
|
||||
return mCompatibilityUuid;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.service;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.Email;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
|
@ -25,6 +26,9 @@ import android.content.Intent;
|
|||
public class BootReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// Restore accounts, if it has not happened already
|
||||
AccountBackupRestore.restoreAccountsIfNeeded(context);
|
||||
|
||||
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
||||
// Returns true if there are any accounts
|
||||
if (Email.setServicesEnabled(context)) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email.service;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.Controller;
|
||||
import com.android.email.Email;
|
||||
import com.android.email.R;
|
||||
|
@ -149,6 +150,9 @@ public class MailService extends Service {
|
|||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
|
||||
// Restore accounts, if it has not happened already
|
||||
AccountBackupRestore.restoreAccountsIfNeeded(this);
|
||||
|
||||
// TODO this needs to be passed through the controller and back to us
|
||||
this.mStartId = startId;
|
||||
String action = intent.getAction();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package com.android.exchange;
|
||||
|
||||
import com.android.email.AccountBackupRestore;
|
||||
import com.android.email.mail.MessagingException;
|
||||
import com.android.email.mail.store.TrustManagerFactory;
|
||||
import com.android.email.provider.EmailContent;
|
||||
|
@ -826,6 +827,10 @@ public class SyncManager extends Service implements Runnable {
|
|||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
alwaysLog("!!! EAS SyncManager, onStartCommand");
|
||||
|
||||
// Restore accounts, if it has not happened already
|
||||
AccountBackupRestore.restoreAccountsIfNeeded(this);
|
||||
|
||||
maybeStartSyncManagerThread();
|
||||
if (sServiceThread == null) {
|
||||
alwaysLog("!!! EAS SyncManager, stopping self");
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email;
|
||||
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailProvider;
|
||||
import com.android.email.provider.ProviderTestUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.test.ProviderTestCase2;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
||||
/**
|
||||
* This is a series of unit tests for backup/restore of the Account class.
|
||||
*
|
||||
* Technically these are functional because they use the underlying preferences framework.
|
||||
*
|
||||
* NOTE: These tests are destructive of any "legacy" accounts that might be lying around.
|
||||
*/
|
||||
@MediumTest
|
||||
public class AccountBackupRestoreTests extends ProviderTestCase2<EmailProvider> {
|
||||
|
||||
private Preferences mPreferences;
|
||||
private Context mMockContext;
|
||||
|
||||
public AccountBackupRestoreTests() {
|
||||
super(EmailProvider.class, EmailProvider.EMAIL_AUTHORITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mMockContext = getMockContext();
|
||||
// Note: preferences are not supported by this mock context, so we must
|
||||
// explicitly use (and clean out) the real ones for now.
|
||||
mPreferences = Preferences.getPreferences(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete any dummy accounts we set up for this test
|
||||
*/
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
deleteLegacyAccounts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete *all* legacy accounts
|
||||
*/
|
||||
private void deleteLegacyAccounts() {
|
||||
Account[] oldAccounts = mPreferences.getAccounts();
|
||||
for (Account oldAccount : oldAccounts) {
|
||||
oldAccount.delete(mPreferences);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test backup with no accounts
|
||||
*/
|
||||
public void testNoAccountBackup() {
|
||||
// create some "old" backups or legacy accounts
|
||||
Account backupAccount = new Account(mMockContext);
|
||||
backupAccount.save(mPreferences);
|
||||
// confirm they are there
|
||||
Account[] oldBackups = mPreferences.getAccounts();
|
||||
assertTrue(oldBackups.length >= 1);
|
||||
// make sure there are no accounts in the provider
|
||||
int numAccounts = EmailContent.count(mMockContext, EmailContent.Account.CONTENT_URI,
|
||||
null, null);
|
||||
assertEquals(0, numAccounts);
|
||||
// run backups
|
||||
AccountBackupRestore.doBackupAccounts(mMockContext, mPreferences);
|
||||
// confirm there are no backups made
|
||||
Account[] backups = mPreferences.getAccounts();
|
||||
assertEquals(0, backups.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test backup with accounts
|
||||
*/
|
||||
public void testBackup() {
|
||||
// Clear the decks
|
||||
deleteLegacyAccounts();
|
||||
|
||||
// Create real accounts in need of backup
|
||||
EmailContent.Account liveAccount1 =
|
||||
ProviderTestUtils.setupAccount("testBackup1", false, mMockContext);
|
||||
liveAccount1.mHostAuthRecv =
|
||||
ProviderTestUtils.setupHostAuth("legacy-recv", 0, false, mMockContext);
|
||||
liveAccount1.mHostAuthSend =
|
||||
ProviderTestUtils.setupHostAuth("legacy-send", 0, false, mMockContext);
|
||||
liveAccount1.setDefaultAccount(true);
|
||||
liveAccount1.save(mMockContext);
|
||||
EmailContent.Account liveAccount2 =
|
||||
ProviderTestUtils.setupAccount("testBackup2", false, mMockContext);
|
||||
liveAccount2.mHostAuthRecv =
|
||||
ProviderTestUtils.setupHostAuth("legacy-recv", 0, false, mMockContext);
|
||||
liveAccount2.mHostAuthSend =
|
||||
ProviderTestUtils.setupHostAuth("legacy-send", 0, false, mMockContext);
|
||||
liveAccount2.setDefaultAccount(false);
|
||||
liveAccount2.save(mMockContext);
|
||||
|
||||
// run backups
|
||||
AccountBackupRestore.doBackupAccounts(mMockContext, mPreferences);
|
||||
|
||||
// Confirm we have two backups now
|
||||
// Deep inspection is not performed here - see LegacyConversionsTests
|
||||
// We just check for basic identity & flags
|
||||
Account[] backups = mPreferences.getAccounts();
|
||||
assertEquals(2, backups.length);
|
||||
for (Account backup : backups) {
|
||||
if ("testBackup1".equals(backup.getDescription())) {
|
||||
assertTrue(0 != (backup.mBackupFlags & Account.BACKUP_FLAGS_IS_DEFAULT));
|
||||
} else if ("testBackup2".equals(backup.getDescription())) {
|
||||
assertFalse(0 != (backup.mBackupFlags & Account.BACKUP_FLAGS_IS_DEFAULT));
|
||||
} else {
|
||||
fail("unexpected backup name=" + backup.getDescription());
|
||||
}
|
||||
}
|
||||
Account backup1 = backups[0];
|
||||
assertTrue(0 != (backup1.mBackupFlags & Account.BACKUP_FLAGS_IS_BACKUP));
|
||||
assertEquals(liveAccount1.getDisplayName(), backup1.getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Test backup EAS accounts, with and without contacts sync
|
||||
*
|
||||
* Blocker: We need to inject the dependency on ContentResolver.getSyncAutomatically()
|
||||
* so we can make our fake accounts appear to be syncable or non-syncable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test no-restore with accounts found
|
||||
*/
|
||||
public void testNoAccountRestore1() {
|
||||
// make sure there are no real backups
|
||||
deleteLegacyAccounts();
|
||||
|
||||
// make sure there are test backups available
|
||||
Account backupAccount1 = setupLegacyBackupAccount("backup1");
|
||||
backupAccount1.save(mPreferences);
|
||||
Account backupAccount2 = setupLegacyBackupAccount("backup2");
|
||||
backupAccount2.save(mPreferences);
|
||||
|
||||
// make sure there are accounts
|
||||
EmailContent.Account existing =
|
||||
ProviderTestUtils.setupAccount("existing", true, mMockContext);
|
||||
|
||||
// run the restore
|
||||
boolean anyRestored = AccountBackupRestore.doRestoreAccounts(mMockContext, mPreferences);
|
||||
assertFalse(anyRestored);
|
||||
|
||||
// make sure accounts still there
|
||||
int numAccounts = EmailContent.count(mMockContext, EmailContent.Account.CONTENT_URI,
|
||||
null, null);
|
||||
assertEquals(1, numAccounts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test no-restore with no accounts & no backups
|
||||
*/
|
||||
public void testNoAccountRestore2() {
|
||||
// make sure there are no real backups
|
||||
deleteLegacyAccounts();
|
||||
|
||||
// make sure there are no accounts
|
||||
int numAccounts = EmailContent.count(mMockContext, EmailContent.Account.CONTENT_URI,
|
||||
null, null);
|
||||
assertEquals(0, numAccounts);
|
||||
|
||||
// run the restore
|
||||
boolean anyRestored = AccountBackupRestore.doRestoreAccounts(mMockContext, mPreferences);
|
||||
assertFalse(anyRestored);
|
||||
|
||||
// make sure accounts still there
|
||||
numAccounts = EmailContent.count(mMockContext, EmailContent.Account.CONTENT_URI,
|
||||
null, null);
|
||||
assertEquals(0, numAccounts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test restore with 2 accounts.
|
||||
* Repeats test to verify restore of default account
|
||||
*/
|
||||
public void testAccountRestore() {
|
||||
// make sure there are no real backups
|
||||
deleteLegacyAccounts();
|
||||
|
||||
// create test backups
|
||||
Account backupAccount1 = setupLegacyBackupAccount("backup1");
|
||||
backupAccount1.mBackupFlags |= Account.BACKUP_FLAGS_IS_DEFAULT;
|
||||
backupAccount1.save(mPreferences);
|
||||
Account backupAccount2 = setupLegacyBackupAccount("backup2");
|
||||
backupAccount2.save(mPreferences);
|
||||
|
||||
// run the restore
|
||||
boolean anyRestored = AccountBackupRestore.doRestoreAccounts(mMockContext, mPreferences);
|
||||
assertTrue(anyRestored);
|
||||
|
||||
// Check the restored accounts
|
||||
// Deep inspection is not performed here - see LegacyConversionsTests for that
|
||||
// We just check for basic identity & flags
|
||||
Cursor c = mMockContext.getContentResolver().query(EmailContent.Account.CONTENT_URI,
|
||||
EmailContent.Account.CONTENT_PROJECTION, null, null, null);
|
||||
try {
|
||||
assertEquals(2, c.getCount());
|
||||
while (c.moveToNext()) {
|
||||
EmailContent.Account restored =
|
||||
EmailContent.getContent(c, EmailContent.Account.class);
|
||||
if ("backup1".equals(restored.getDisplayName())) {
|
||||
assertTrue(restored.mIsDefault);
|
||||
} else if ("backup2".equals(restored.getDisplayName())) {
|
||||
assertFalse(restored.mIsDefault);
|
||||
} else {
|
||||
fail("Unexpected restore account name=" + restored.getDisplayName());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
|
||||
// clear out the backups & accounts and try again
|
||||
deleteLegacyAccounts();
|
||||
mMockContext.getContentResolver().delete(EmailContent.Account.CONTENT_URI, null, null);
|
||||
|
||||
Account backupAccount3 = setupLegacyBackupAccount("backup3");
|
||||
backupAccount3.save(mPreferences);
|
||||
Account backupAccount4 = setupLegacyBackupAccount("backup4");
|
||||
backupAccount4.mBackupFlags |= Account.BACKUP_FLAGS_IS_DEFAULT;
|
||||
backupAccount4.save(mPreferences);
|
||||
|
||||
// run the restore
|
||||
AccountBackupRestore.doRestoreAccounts(mMockContext, mPreferences);
|
||||
|
||||
// Check the restored accounts
|
||||
// Deep inspection is not performed here - see LegacyConversionsTests for that
|
||||
// We just check for basic identity & flags
|
||||
c = mMockContext.getContentResolver().query(EmailContent.Account.CONTENT_URI,
|
||||
EmailContent.Account.CONTENT_PROJECTION, null, null, null);
|
||||
try {
|
||||
assertEquals(2, c.getCount());
|
||||
while (c.moveToNext()) {
|
||||
EmailContent.Account restored =
|
||||
EmailContent.getContent(c, EmailContent.Account.class);
|
||||
if ("backup3".equals(restored.getDisplayName())) {
|
||||
assertFalse(restored.mIsDefault);
|
||||
} else if ("backup4".equals(restored.getDisplayName())) {
|
||||
assertTrue(restored.mIsDefault);
|
||||
} else {
|
||||
fail("Unexpected restore account name=" + restored.getDisplayName());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Test restore EAS accounts, with and without contacts sync
|
||||
*
|
||||
* Blocker: We need to inject the dependency on account manager to catch the calls to it
|
||||
*/
|
||||
|
||||
/**
|
||||
* Setup a legacy backup account with many fields prefilled.
|
||||
*/
|
||||
private Account setupLegacyBackupAccount(String name) {
|
||||
Account backup = new Account(mMockContext);
|
||||
|
||||
// fill in useful fields
|
||||
backup.mUuid = "test-uid-" + name;
|
||||
backup.mStoreUri = "store://test/" + name;
|
||||
backup.mLocalStoreUri = "local://localhost/" + name;
|
||||
backup.mSenderUri = "sender://test/" + name;
|
||||
backup.mDescription = name;
|
||||
backup.mName = "name " + name;
|
||||
backup.mEmail = "email " + name;
|
||||
backup.mAutomaticCheckIntervalMinutes = 100;
|
||||
backup.mLastAutomaticCheckTime = 200;
|
||||
backup.mNotifyNewMail = true;
|
||||
backup.mDraftsFolderName = "drafts " + name;
|
||||
backup.mSentFolderName = "sent " + name;
|
||||
backup.mTrashFolderName = "trash " + name;
|
||||
backup.mOutboxFolderName = "outbox " + name;
|
||||
backup.mAccountNumber = 300;
|
||||
backup.mVibrate = true;
|
||||
backup.mRingtoneUri = "ringtone://test/" + name;
|
||||
backup.mSyncWindow = 400;
|
||||
backup.mBackupFlags = Account.BACKUP_FLAGS_IS_BACKUP;
|
||||
backup.mProtocolVersion = "proto version" + name;
|
||||
backup.mDeletePolicy = Account.DELETE_POLICY_NEVER;
|
||||
return backup;
|
||||
}
|
||||
}
|
|
@ -134,7 +134,37 @@ public class AccountUnitTests extends AndroidTestCase {
|
|||
storedPolicy = mPreferences.mSharedPreferences.getInt(mUuid + ".deletePolicy", -1);
|
||||
assertEquals(Account.DELETE_POLICY_ON_DELETE, storedPolicy);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test new flags field (added only for backups - not used by real/legacy accounts)
|
||||
*/
|
||||
public void testFlagsField() {
|
||||
createTestAccount();
|
||||
assertEquals(0, mAccount.mBackupFlags);
|
||||
mAccount.save(mPreferences);
|
||||
mAccount.mBackupFlags = -1;
|
||||
mAccount.refresh(mPreferences);
|
||||
assertEquals(0, mAccount.mBackupFlags);
|
||||
|
||||
mAccount.mBackupFlags = Account.BACKUP_FLAGS_IS_BACKUP;
|
||||
mAccount.save(mPreferences);
|
||||
mAccount.mBackupFlags = -1;
|
||||
mAccount.refresh(mPreferences);
|
||||
assertEquals(Account.BACKUP_FLAGS_IS_BACKUP, mAccount.mBackupFlags);
|
||||
|
||||
mAccount.mBackupFlags = Account.BACKUP_FLAGS_SYNC_CONTACTS;
|
||||
mAccount.save(mPreferences);
|
||||
mAccount.mBackupFlags = -1;
|
||||
mAccount.refresh(mPreferences);
|
||||
assertEquals(Account.BACKUP_FLAGS_SYNC_CONTACTS, mAccount.mBackupFlags);
|
||||
|
||||
mAccount.mBackupFlags = Account.BACKUP_FLAGS_IS_DEFAULT;
|
||||
mAccount.save(mPreferences);
|
||||
mAccount.mBackupFlags = -1;
|
||||
mAccount.refresh(mPreferences);
|
||||
assertEquals(Account.BACKUP_FLAGS_IS_DEFAULT, mAccount.mBackupFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dummy account with minimal fields
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.email;
|
||||
|
||||
import com.android.email.Account;
|
||||
import com.android.email.mail.Address;
|
||||
import com.android.email.mail.BodyPart;
|
||||
import com.android.email.mail.Flag;
|
||||
|
@ -65,13 +66,14 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
private static final String RECIPIENT_BCC = "recipient-bcc@android.com";
|
||||
private static final String REPLY_TO = "reply-to@android.com";
|
||||
private static final String SUBJECT = "This is the subject";
|
||||
private static final String BODY = "This is the body. This is also the body.";
|
||||
private static final String MESSAGE_ID = "Test-Message-ID";
|
||||
private static final String MESSAGE_ID_2 = "Test-Message-ID-Second";
|
||||
|
||||
EmailProvider mProvider;
|
||||
Context mProviderContext;
|
||||
Context mContext;
|
||||
Account mLegacyAccount = null;
|
||||
Preferences mPreferences = null;
|
||||
|
||||
public LegacyConversionsTests() {
|
||||
super(EmailProvider.class, EmailProvider.EMAIL_AUTHORITY);
|
||||
|
@ -87,6 +89,9 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
if (mLegacyAccount != null) {
|
||||
mLegacyAccount.delete(mPreferences);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,7 +160,7 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
}
|
||||
if (sender != null) {
|
||||
Address[] addresses = Address.parse(sender);
|
||||
message.setFrom(Address.parse(sender)[0]);
|
||||
message.setFrom(addresses[0]);
|
||||
}
|
||||
if (subject != null) {
|
||||
message.setSubject(subject);
|
||||
|
@ -480,4 +485,130 @@ public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
|
|||
|
||||
// cv.put("attachment_count", attachments.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test conversion of a legacy account to a provider account
|
||||
*/
|
||||
public void testMakeProviderAccount() throws MessagingException {
|
||||
|
||||
setupLegacyAccount("testMakeProviderAccount", true);
|
||||
EmailContent.Account toAccount =
|
||||
LegacyConversions.makeAccount(mProviderContext, mLegacyAccount);
|
||||
checkProviderAccount("testMakeProviderAccount", mLegacyAccount, toAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test conversion of a provider account to a legacy account
|
||||
*/
|
||||
public void testMakeLegacyAccount() throws MessagingException {
|
||||
EmailContent.Account fromAccount = ProviderTestUtils.setupAccount("convert-to-legacy",
|
||||
false, mProviderContext);
|
||||
fromAccount.mHostAuthRecv =
|
||||
ProviderTestUtils.setupHostAuth("legacy-recv", 0, false, mProviderContext);
|
||||
fromAccount.mHostAuthSend =
|
||||
ProviderTestUtils.setupHostAuth("legacy-send", 0, false, mProviderContext);
|
||||
fromAccount.save(mProviderContext);
|
||||
|
||||
Account toAccount = LegacyConversions.makeLegacyAccount(mProviderContext, fromAccount);
|
||||
checkLegacyAccount("testMakeLegacyAccount", fromAccount, toAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a legacy account in mLegacyAccount with many fields prefilled.
|
||||
*/
|
||||
private void setupLegacyAccount(String name, boolean saveIt) {
|
||||
// prefs & legacy account are saved for cleanup (it's stored in the real prefs file)
|
||||
mPreferences = Preferences.getPreferences(mProviderContext);
|
||||
mLegacyAccount = new Account(mProviderContext);
|
||||
|
||||
// fill in useful fields
|
||||
mLegacyAccount.mUuid = "test-uid-" + name;
|
||||
mLegacyAccount.mStoreUri = "store://test/" + name;
|
||||
mLegacyAccount.mLocalStoreUri = "local://localhost/" + name;
|
||||
mLegacyAccount.mSenderUri = "sender://test/" + name;
|
||||
mLegacyAccount.mDescription = "description " + name;
|
||||
mLegacyAccount.mName = "name " + name;
|
||||
mLegacyAccount.mEmail = "email " + name;
|
||||
mLegacyAccount.mAutomaticCheckIntervalMinutes = 100;
|
||||
mLegacyAccount.mLastAutomaticCheckTime = 200;
|
||||
mLegacyAccount.mNotifyNewMail = true;
|
||||
mLegacyAccount.mDraftsFolderName = "drafts " + name;
|
||||
mLegacyAccount.mSentFolderName = "sent " + name;
|
||||
mLegacyAccount.mTrashFolderName = "trash " + name;
|
||||
mLegacyAccount.mOutboxFolderName = "outbox " + name;
|
||||
mLegacyAccount.mAccountNumber = 300;
|
||||
mLegacyAccount.mVibrate = true;
|
||||
mLegacyAccount.mRingtoneUri = "ringtone://test/" + name;
|
||||
mLegacyAccount.mSyncWindow = 400;
|
||||
mLegacyAccount.mBackupFlags = 0;
|
||||
mLegacyAccount.mDeletePolicy = Account.DELETE_POLICY_NEVER;
|
||||
|
||||
if (saveIt) {
|
||||
mLegacyAccount.save(mPreferences);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare a provider account to the legacy account it was created from
|
||||
*/
|
||||
private void checkProviderAccount(String tag, Account expect, EmailContent.Account actual)
|
||||
throws MessagingException {
|
||||
assertEquals(tag + " description", expect.getDescription(), actual.mDisplayName);
|
||||
assertEquals(tag + " email", expect.getEmail(), actual.mEmailAddress);
|
||||
assertEquals(tag + " sync key", "", actual.mSyncKey);
|
||||
assertEquals(tag + " lookback", expect.getSyncWindow(), actual.mSyncLookback);
|
||||
assertEquals(tag + " sync intvl", expect.getAutomaticCheckIntervalMinutes(),
|
||||
actual.mSyncInterval);
|
||||
// These asserts are checking mHostAuthKeyRecv & mHostAuthKeySend
|
||||
assertEquals(tag + " store", expect.getStoreUri(), actual.getStoreUri(mProviderContext));
|
||||
assertEquals(tag + " sender", expect.getSenderUri(), actual.getSenderUri(mProviderContext));
|
||||
// Synthesize & check flags
|
||||
int expectFlags = 0;
|
||||
if (expect.mNotifyNewMail) expectFlags |= EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL;
|
||||
if (expect.mVibrate) expectFlags |= EmailContent.Account.FLAGS_VIBRATE;
|
||||
expectFlags |=
|
||||
(expect.mDeletePolicy << EmailContent.Account.FLAGS_DELETE_POLICY_SHIFT)
|
||||
& EmailContent.Account.FLAGS_DELETE_POLICY_MASK;
|
||||
assertEquals(tag + " flags", expectFlags, actual.mFlags);
|
||||
assertEquals(tag + " default", false, actual.mIsDefault);
|
||||
assertEquals(tag + " uuid", expect.getUuid(), actual.mCompatibilityUuid);
|
||||
assertEquals(tag + " name", expect.getName(), actual.mSenderName);
|
||||
assertEquals(tag + " ringtone", expect.getRingtone(), actual.mRingtoneUri);
|
||||
assertEquals(tag + " proto vers", expect.mProtocolVersion, actual.mProtocolVersion);
|
||||
assertEquals(tag + " new count", 0, actual.mNewMessageCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare a legacy account to the provider account it was created from
|
||||
*/
|
||||
private void checkLegacyAccount(String tag, EmailContent.Account expect, Account actual)
|
||||
throws MessagingException {
|
||||
int expectFlags = expect.getFlags();
|
||||
|
||||
assertEquals(tag + " uuid", expect.mCompatibilityUuid, actual.mUuid);
|
||||
assertEquals(tag + " store", expect.getStoreUri(mProviderContext), actual.mStoreUri);
|
||||
assertTrue(actual.mLocalStoreUri.startsWith("local://localhost"));
|
||||
assertEquals(tag + " sender", expect.getSenderUri(mProviderContext), actual.mSenderUri);
|
||||
assertEquals(tag + " description", expect.getDisplayName(), actual.mDescription);
|
||||
assertEquals(tag + " name", expect.getSenderName(), actual.mName);
|
||||
assertEquals(tag + " email", expect.getEmailAddress(), actual.mEmail);
|
||||
assertEquals(tag + " checkintvl", expect.getSyncInterval(),
|
||||
actual.mAutomaticCheckIntervalMinutes);
|
||||
assertEquals(tag + " checktime", 0, actual.mLastAutomaticCheckTime);
|
||||
assertEquals(tag + " notify",
|
||||
(expectFlags & EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL) != 0,
|
||||
actual.mNotifyNewMail);
|
||||
assertEquals(tag + " drafts", null, actual.mDraftsFolderName);
|
||||
assertEquals(tag + " sent", null, actual.mSentFolderName);
|
||||
assertEquals(tag + " trash", null, actual.mTrashFolderName);
|
||||
assertEquals(tag + " outbox", null, actual.mOutboxFolderName);
|
||||
assertEquals(tag + " acct #", -1, actual.mAccountNumber);
|
||||
assertEquals(tag + " vibrate", (expectFlags & EmailContent.Account.FLAGS_VIBRATE) != 0,
|
||||
actual.mVibrate);
|
||||
assertEquals(tag + " ", expect.getRingtone(), actual.mRingtoneUri);
|
||||
assertEquals(tag + " sync window", expect.getSyncLookback(), actual.mSyncWindow);
|
||||
assertEquals(tag + " backup flags", 0, actual.mBackupFlags);
|
||||
assertEquals(tag + " proto vers", expect.mProtocolVersion, actual.mProtocolVersion);
|
||||
assertEquals(tag + " delete policy", expect.getDeletePolicy(), actual.getDeletePolicy());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.android.email;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
@ -34,7 +33,6 @@ public class PreferencesUnitTests extends AndroidTestCase {
|
|||
|
||||
private Preferences mPreferences;
|
||||
|
||||
private String mUuid;
|
||||
private Account mAccount;
|
||||
|
||||
@Override
|
||||
|
@ -94,8 +92,6 @@ public class PreferencesUnitTests extends AndroidTestCase {
|
|||
private void createTestAccount() {
|
||||
mAccount = new Account(getContext());
|
||||
mAccount.save(mPreferences);
|
||||
|
||||
mUuid = mAccount.getUuid();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue