summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Stadler <stadler@android.com>2010-01-20 11:25:28 -0800
committerAndroid Git Automerger <android-git-automerger@android.com>2010-01-20 11:25:28 -0800
commitd3998ebe4e3581bbd34669fa5fb78537fa6ec98b (patch)
tree8f417a19e1eb22a25f8cb8278b29909d5bebcaf9
parent80eb4fd0fed32800cd0c78e0e26ca0b3d54c7d2b (diff)
parent85d765f41c20e6efb3b120055eb4f1efae166e8b (diff)
downloadandroid_packages_apps_Email-d3998ebe4e3581bbd34669fa5fb78537fa6ec98b.tar.gz
android_packages_apps_Email-d3998ebe4e3581bbd34669fa5fb78537fa6ec98b.tar.bz2
android_packages_apps_Email-d3998ebe4e3581bbd34669fa5fb78537fa6ec98b.zip
am 85d765f4: Quick backup/restore of accounts DO NOT MERGE
Merge commit '85d765f41c20e6efb3b120055eb4f1efae166e8b' into eclair-plus-aosp * commit '85d765f41c20e6efb3b120055eb4f1efae166e8b': Quick backup/restore of accounts DO NOT MERGE
-rw-r--r--src/com/android/email/Account.java28
-rw-r--r--src/com/android/email/AccountBackupRestore.java224
-rw-r--r--src/com/android/email/LegacyConversions.java80
-rw-r--r--src/com/android/email/activity/AccountFolderList.java3
-rw-r--r--src/com/android/email/activity/Welcome.java8
-rw-r--r--src/com/android/email/activity/setup/AccountSettingsUtils.java3
-rw-r--r--src/com/android/email/activity/setup/AccountSetupBasics.java3
-rw-r--r--src/com/android/email/activity/setup/AccountSetupExchange.java3
-rw-r--r--src/com/android/email/activity/setup/AccountSetupIncoming.java3
-rw-r--r--src/com/android/email/activity/setup/AccountSetupNames.java3
-rw-r--r--src/com/android/email/activity/setup/AccountSetupOutgoing.java3
-rw-r--r--src/com/android/email/mail/store/ExchangeStore.java22
-rw-r--r--src/com/android/email/provider/EmailContent.java2
-rw-r--r--src/com/android/email/service/BootReceiver.java4
-rw-r--r--src/com/android/email/service/MailService.java4
-rw-r--r--src/com/android/exchange/SyncManager.java5
-rw-r--r--tests/src/com/android/email/AccountBackupRestoreTests.java311
-rw-r--r--tests/src/com/android/email/AccountUnitTests.java32
-rw-r--r--tests/src/com/android/email/LegacyConversionsTests.java135
-rw-r--r--tests/src/com/android/email/PreferencesUnitTests.java4
20 files changed, 861 insertions, 19 deletions
diff --git a/src/com/android/email/Account.java b/src/com/android/email/Account.java
index cea3a25d6..236db1a4c 100644
--- a/src/com/android/email/Account.java
+++ b/src/com/android/email/Account.java
@@ -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.
diff --git a/src/com/android/email/AccountBackupRestore.java b/src/com/android/email/AccountBackupRestore.java
new file mode 100644
index 000000000..376c29f5c
--- /dev/null
+++ b/src/com/android/email/AccountBackupRestore.java
@@ -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;
+ }
+}
diff --git a/src/com/android/email/LegacyConversions.java b/src/com/android/email/LegacyConversions.java
index 4c1ccf855..6c424df51 100644
--- a/src/com/android/email/LegacyConversions.java
+++ b/src/com/android/email/LegacyConversions.java
@@ -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;
+ }
}
diff --git a/src/com/android/email/activity/AccountFolderList.java b/src/com/android/email/activity/AccountFolderList.java
index b321dfe29..08ae5fca0 100644
--- a/src/com/android/email/activity/AccountFolderList.java
+++ b/src/com/android/email/activity/AccountFolderList.java
@@ -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;
@@ -460,6 +461,8 @@ public class AccountFolderList extends ListActivity
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
}
diff --git a/src/com/android/email/activity/Welcome.java b/src/com/android/email/activity/Welcome.java
index 0bf77b3a7..7fd15b261 100644
--- a/src/com/android/email/activity/Welcome.java
+++ b/src/com/android/email/activity/Welcome.java
@@ -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;
@@ -42,6 +43,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
diff --git a/src/com/android/email/activity/setup/AccountSettingsUtils.java b/src/com/android/email/activity/setup/AccountSettingsUtils.java
index 99b779786..57f1cac00 100644
--- a/src/com/android/email/activity/setup/AccountSettingsUtils.java
+++ b/src/com/android/email/activity/setup/AccountSettingsUtils.java
@@ -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);
}
}
diff --git a/src/com/android/email/activity/setup/AccountSetupBasics.java b/src/com/android/email/activity/setup/AccountSetupBasics.java
index e29b73f76..2b805c72f 100644
--- a/src/com/android/email/activity/setup/AccountSetupBasics.java
+++ b/src/com/android/email/activity/setup/AccountSetupBasics.java
@@ -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;
@@ -380,6 +381,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();
diff --git a/src/com/android/email/activity/setup/AccountSetupExchange.java b/src/com/android/email/activity/setup/AccountSetupExchange.java
index dfeca770e..c89f8acca 100644
--- a/src/com/android/email/activity/setup/AccountSetupExchange.java
+++ b/src/com/android/email/activity/setup/AccountSetupExchange.java
@@ -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;
@@ -288,6 +289,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
diff --git a/src/com/android/email/activity/setup/AccountSetupIncoming.java b/src/com/android/email/activity/setup/AccountSetupIncoming.java
index f8625e15e..f93d49b3a 100644
--- a/src/com/android/email/activity/setup/AccountSetupIncoming.java
+++ b/src/com/android/email/activity/setup/AccountSetupIncoming.java
@@ -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 {
/*
diff --git a/src/com/android/email/activity/setup/AccountSetupNames.java b/src/com/android/email/activity/setup/AccountSetupNames.java
index b99cf0550..2486a381b 100644
--- a/src/com/android/email/activity/setup/AccountSetupNames.java
+++ b/src/com/android/email/activity/setup/AccountSetupNames.java
@@ -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.activity.MessageList;
@@ -117,6 +118,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);
// Exit or dispatch per flow mode
if (getIntent().getBooleanExtra(EXTRA_EAS_FLOW, false)) {
diff --git a/src/com/android/email/activity/setup/AccountSetupOutgoing.java b/src/com/android/email/activity/setup/AccountSetupOutgoing.java
index 0b2138c07..40e369efd 100644
--- a/src/com/android/email/activity/setup/AccountSetupOutgoing.java
+++ b/src/com/android/email/activity/setup/AccountSetupOutgoing.java
@@ -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);
diff --git a/src/com/android/email/mail/store/ExchangeStore.java b/src/com/android/email/mail/store/ExchangeStore.java
index 69f6e60d1..013287897 100644
--- a/src/com/android/email/mail/store/ExchangeStore.java
+++ b/src/com/android/email/mail/store/ExchangeStore.java
@@ -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;
diff --git a/src/com/android/email/provider/EmailContent.java b/src/com/android/email/provider/EmailContent.java
index 955ea0d27..cc060c72d 100644
--- a/src/com/android/email/provider/EmailContent.java
+++ b/src/com/android/email/provider/EmailContent.java
@@ -1066,7 +1066,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;
}
diff --git a/src/com/android/email/service/BootReceiver.java b/src/com/android/email/service/BootReceiver.java
index 74d569f07..880773c2b 100644
--- a/src/com/android/email/service/BootReceiver.java
+++ b/src/com/android/email/service/BootReceiver.java
@@ -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)) {
diff --git a/src/com/android/email/service/MailService.java b/src/com/android/email/service/MailService.java
index 48c18bf22..557ce8fb7 100644
--- a/src/com/android/email/service/MailService.java
+++ b/src/com/android/email/service/MailService.java
@@ -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();
diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java
index e83dba5f2..0133aafaf 100644
--- a/src/com/android/exchange/SyncManager.java
+++ b/src/com/android/exchange/SyncManager.java
@@ -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;
@@ -816,6 +817,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");
diff --git a/tests/src/com/android/email/AccountBackupRestoreTests.java b/tests/src/com/android/email/AccountBackupRestoreTests.java
new file mode 100644
index 000000000..47466e716
--- /dev/null
+++ b/tests/src/com/android/email/AccountBackupRestoreTests.java
@@ -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;
+ }
+}
diff --git a/tests/src/com/android/email/AccountUnitTests.java b/tests/src/com/android/email/AccountUnitTests.java
index ff097993b..ef1797a7c 100644
--- a/tests/src/com/android/email/AccountUnitTests.java
+++ b/tests/src/com/android/email/AccountUnitTests.java
@@ -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
*/
diff --git a/tests/src/com/android/email/LegacyConversionsTests.java b/tests/src/com/android/email/LegacyConversionsTests.java
index 3fceb32b3..e487ffeac 100644
--- a/tests/src/com/android/email/LegacyConversionsTests.java
+++ b/tests/src/com/android/email/LegacyConversionsTests.java
@@ -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());
+ }
}
diff --git a/tests/src/com/android/email/PreferencesUnitTests.java b/tests/src/com/android/email/PreferencesUnitTests.java
index 3216ccea1..c9129f799 100644
--- a/tests/src/com/android/email/PreferencesUnitTests.java
+++ b/tests/src/com/android/email/PreferencesUnitTests.java
@@ -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();
}
}