diff --git a/src/com/android/email/Account.java b/src/com/android/email/Account.java index c1e8cb2e4..0e5536948 100644 --- a/src/com/android/email/Account.java +++ b/src/com/android/email/Account.java @@ -30,16 +30,24 @@ 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 implements Serializable, Store.PersistentDataCallbacks { 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; public static final int CHECK_INTERVAL_NEVER = -1; public static final int CHECK_INTERVAL_PUSH = -2; - - private static final long serialVersionUID = 2975156672298625121L; + /** + * 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; + + // transient values - do not serialize + private transient Preferences mPreferences; + + // serialized values String mUuid; String mStoreUri; String mLocalStoreUri; @@ -57,6 +65,12 @@ public class Account implements Serializable { int mAccountNumber; boolean mVibrate; String mRingtoneUri; + String mStorePersistent; + + /** + * TODO: all fields should be tagged here + */ + private final String PREF_TAG_STORE_PERSISTENT = ".storePersist"; /** *
@@ -87,6 +101,8 @@ public class Account implements Serializable {
      * Refresh the account from the stored settings.
      */
     public void refresh(Preferences preferences) {
+        mPreferences = preferences;
+
         mStoreUri = Utility.base64Decode(preferences.mSharedPreferences.getString(mUuid
                 + ".storeUri", null));
         mLocalStoreUri = preferences.mSharedPreferences.getString(mUuid + ".localStoreUri", null);
@@ -129,6 +145,9 @@ public class Account implements Serializable {
         mVibrate = preferences.mSharedPreferences.getBoolean(mUuid + ".vibrate", false);
         mRingtoneUri = preferences.mSharedPreferences.getString(mUuid  + ".ringtone", 
                 "content://settings/system/notification_sound");
+
+        mStorePersistent = preferences.mSharedPreferences.getString(
+                mUuid  + PREF_TAG_STORE_PERSISTENT, null);
     }
 
     public String getUuid() {
@@ -223,7 +242,8 @@ public class Account implements Serializable {
         editor.remove(mUuid + ".accountNumber");
         editor.remove(mUuid + ".vibrate");
         editor.remove(mUuid + ".ringtone");
-        
+        editor.remove(mUuid + PREF_TAG_STORE_PERSISTENT);
+
         // also delete any deprecated fields
         editor.remove(mUuid + ".transportUri");
         
@@ -231,6 +251,8 @@ public class Account implements Serializable {
     }
 
     public void save(Preferences preferences) {
+        mPreferences = preferences;
+        
         if (!preferences.mSharedPreferences.getString("accountUuids", "").contains(mUuid)) {
             /*
              * When the account is first created we assign it a unique account number. The
@@ -284,12 +306,17 @@ public class Account implements Serializable {
         editor.putBoolean(mUuid + ".vibrate", mVibrate);
         editor.putString(mUuid + ".ringtone", mRingtoneUri);
         
+        // The following fields are *not* written because they need to be more fine-grained
+        // and not risk rewriting with old data.
+        // editor.putString(mUuid + PREF_TAG_STORE_PERSISTENT, mStorePersistent);
+
         // also delete any deprecated fields
         editor.remove(mUuid + ".transportUri");
 
         editor.commit();
     }
 
+    @Override
     public String toString() {
         return mDescription;
     }
@@ -380,6 +407,44 @@ public class Account implements Serializable {
         return mAccountNumber;
     }
 
+    /**
+     * Provides a small place for Stores to store persistent data.  This will need to be
+     * expanded in the future, but is sufficient for now.  
+     * @param storeData Data to persist.  All data must be encoded into a string,
+     * so use base64 or some other encoding if necessary.
+     */
+    public void setPersistentString(String storeData) {
+        synchronized (this.getClass()) {
+            mStorePersistent = mPreferences.mSharedPreferences.getString(
+                    mUuid  + PREF_TAG_STORE_PERSISTENT, null);
+            if ((mStorePersistent == null && storeData != null) || 
+                    (mStorePersistent != null && !mStorePersistent.equals(storeData))) {
+                mStorePersistent = storeData;
+                SharedPreferences.Editor editor = mPreferences.mSharedPreferences.edit();
+                editor.putString(mUuid + PREF_TAG_STORE_PERSISTENT, mStorePersistent);
+                editor.commit();
+            }
+        }
+    }
+
+    /**
+     * @return the data saved by the Store, or null if never set.
+     */
+    public String getPersistentString() {
+        synchronized (this.getClass()) {
+            mStorePersistent = mPreferences.mSharedPreferences.getString(
+                    mUuid  + PREF_TAG_STORE_PERSISTENT, null);
+        }
+        return mStorePersistent;
+    }
+    
+    /**
+     * @return An implementation of Store.PersistentDataCallbacks
+     */
+    public Store.PersistentDataCallbacks getStoreCallbacks() {
+        return this;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (o instanceof Account) {
diff --git a/src/com/android/email/MessagingController.java b/src/com/android/email/MessagingController.java
index 46dd9f3fb..cacaad043 100644
--- a/src/com/android/email/MessagingController.java
+++ b/src/com/android/email/MessagingController.java
@@ -189,7 +189,7 @@ public class MessagingController implements Runnable {
             l.listFoldersStarted(account);
         }
         try {
-            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             Folder[] localFolders = localStore.getPersonalNamespaces();
 
             if (localFolders == null || localFolders.length == 0) {
@@ -210,13 +210,13 @@ public class MessagingController implements Runnable {
             put("listFolders", listener, new Runnable() {
                 public void run() {
                     try {
-                        Store store = Store.getInstance(account.getStoreUri(), mApplication);
+                        Store store = Store.getInstance(account.getStoreUri(), mApplication, 
+                                account.getStoreCallbacks());
 
                         Folder[] remoteFolders = store.getPersonalNamespaces();
 
                         Store localStore = Store.getInstance(
-                                account.getLocalStoreUri(),
-                                mApplication);
+                                account.getLocalStoreUri(), mApplication, null);
                         HashSet remoteFolderNames = new HashSet();
                         for (int i = 0, count = remoteFolders.length; i < count; i++) {
                             Folder localFolder = localStore.getFolder(remoteFolders[i].getName());
@@ -284,7 +284,7 @@ public class MessagingController implements Runnable {
         }
 
         try {
-            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             Folder localFolder = localStore.getFolder(folder);
             localFolder.open(OpenMode.READ_WRITE);
             Message[] localMessages = localFolder.getMessages(null);
@@ -311,8 +311,7 @@ public class MessagingController implements Runnable {
     public void loadMoreMessages(Account account, String folder, MessagingListener listener) {
         try {
             LocalStore localStore = (LocalStore) Store.getInstance(
-                    account.getLocalStoreUri(),
-                    mApplication);
+                    account.getLocalStoreUri(), mApplication, null);
             LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
             localFolder.setVisibleLimit(localFolder.getVisibleLimit()
                     + Email.VISIBLE_LIMIT_INCREMENT);
@@ -327,7 +326,7 @@ public class MessagingController implements Runnable {
         for (Account account : accounts) {
             try {
                 LocalStore localStore =
-                    (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication);
+                    (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication, null);
                 localStore.resetVisibleLimits();
             }
             catch (MessagingException e) {
@@ -387,7 +386,7 @@ public class MessagingController implements Runnable {
              * the uids within the list.
              */
             final LocalStore localStore =
-                (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication);
+                (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             final LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
             localFolder.open(OpenMode.READ_WRITE);
             Message[] localMessages = localFolder.getMessages(null);
@@ -396,7 +395,8 @@ public class MessagingController implements Runnable {
                 localUidMap.put(message.getUid(), message);
             }
 
-            Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
+            Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication, 
+                    account.getStoreCallbacks());
             Folder remoteFolder = remoteStore.getFolder(folder);
 
             /*
@@ -753,8 +753,7 @@ public class MessagingController implements Runnable {
     private void queuePendingCommand(Account account, PendingCommand command) {
         try {
             LocalStore localStore = (LocalStore) Store.getInstance(
-                    account.getLocalStoreUri(),
-                    mApplication);
+                    account.getLocalStoreUri(), mApplication, null);
             localStore.addPendingCommand(command);
         }
         catch (Exception e) {
@@ -783,8 +782,7 @@ public class MessagingController implements Runnable {
 
     private void processPendingCommandsSynchronous(Account account) throws MessagingException {
         LocalStore localStore = (LocalStore) Store.getInstance(
-                account.getLocalStoreUri(),
-                mApplication);
+                account.getLocalStoreUri(), mApplication, null);
         ArrayList commands = localStore.getPendingCommands();
         for (PendingCommand command : commands) {
             /*
@@ -823,8 +821,7 @@ public class MessagingController implements Runnable {
         String uid = command.arguments[1];
 
         LocalStore localStore = (LocalStore) Store.getInstance(
-                account.getLocalStoreUri(),
-                mApplication);
+                account.getLocalStoreUri(), mApplication, null);
         LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
         LocalMessage localMessage = (LocalMessage) localFolder.getMessage(uid);
 
@@ -832,7 +829,8 @@ public class MessagingController implements Runnable {
             return;
         }
 
-        Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
+        Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication, 
+                account.getStoreCallbacks());
         Folder remoteFolder = remoteStore.getFolder(folder);
         if (!remoteFolder.exists()) {
             if (!remoteFolder.create(FolderType.HOLDS_MESSAGES)) {
@@ -916,7 +914,8 @@ public class MessagingController implements Runnable {
         String folder = command.arguments[0];
         String uid = command.arguments[1];
 
-        Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
+        Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication, 
+                account.getStoreCallbacks());
         Folder remoteFolder = remoteStore.getFolder(folder);
         if (!remoteFolder.exists()) {
             return;
@@ -966,7 +965,8 @@ public class MessagingController implements Runnable {
         String uid = command.arguments[1];
         boolean read = Boolean.parseBoolean(command.arguments[2]);
 
-        Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
+        Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication, 
+                account.getStoreCallbacks());
         Folder remoteFolder = remoteStore.getFolder(folder);
         if (!remoteFolder.exists()) {
             return;
@@ -999,7 +999,7 @@ public class MessagingController implements Runnable {
             final String uid,
             final boolean seen) {
         try {
-            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             Folder localFolder = localStore.getFolder(folder);
             localFolder.open(OpenMode.READ_WRITE);
 
@@ -1021,7 +1021,8 @@ public class MessagingController implements Runnable {
         put("loadMessageForViewRemote", listener, new Runnable() {
             public void run() {
                 try {
-                    Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+                    Store localStore = Store.getInstance(
+                            account.getLocalStoreUri(), mApplication, null);
                     LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
                     localFolder.open(OpenMode.READ_WRITE);
 
@@ -1052,7 +1053,8 @@ public class MessagingController implements Runnable {
                      * fully if possible.
                      */
 
-                    Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
+                    Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication, 
+                            account.getStoreCallbacks());
                     Folder remoteFolder = remoteStore.getFolder(folder);
                     remoteFolder.open(OpenMode.READ_WRITE);
 
@@ -1099,7 +1101,7 @@ public class MessagingController implements Runnable {
             l.loadMessageForViewStarted(account, folder, uid);
         }
         try {
-            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             LocalFolder localFolder = (LocalFolder) localStore.getFolder(folder);
             localFolder.open(OpenMode.READ_WRITE);
 
@@ -1185,8 +1187,8 @@ public class MessagingController implements Runnable {
         put("loadAttachment", listener, new Runnable() {
             public void run() {
                 try {
-                    LocalStore localStore =
-                        (LocalStore) Store.getInstance(account.getLocalStoreUri(), mApplication);
+                    LocalStore localStore = (LocalStore) Store.getInstance(
+                            account.getLocalStoreUri(), mApplication, null);
                     /*
                      * We clear out any attachments already cached in the entire store and then
                      * we update the passed in message to reflect that there are no cached
@@ -1200,7 +1202,8 @@ public class MessagingController implements Runnable {
                     for (Part attachment : attachments) {
                         attachment.setBody(null);
                     }
-                    Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
+                    Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication, 
+                            account.getStoreCallbacks());
                     LocalFolder localFolder =
                         (LocalFolder) localStore.getFolder(message.getFolder().getName());
                     Folder remoteFolder = remoteStore.getFolder(message.getFolder().getName());
@@ -1238,7 +1241,7 @@ public class MessagingController implements Runnable {
             final Message message,
             MessagingListener listener) {
         try {
-            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             LocalFolder localFolder =
                 (LocalFolder) localStore.getFolder(account.getOutboxFolderName());
             localFolder.open(OpenMode.READ_WRITE);
@@ -1278,9 +1281,7 @@ public class MessagingController implements Runnable {
      */
     public void sendPendingMessagesSynchronous(final Account account) {
         try {
-            Store localStore = Store.getInstance(
-                    account.getLocalStoreUri(),
-                    mApplication);
+            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             Folder localFolder = localStore.getFolder(
                     account.getOutboxFolderName());
             if (!localFolder.exists()) {
@@ -1364,7 +1365,7 @@ public class MessagingController implements Runnable {
             return;
         }
         try {
-            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             Folder localFolder = localStore.getFolder(folder);
             Folder localTrashFolder = localStore.getFolder(account.getTrashFolderName());
 
@@ -1389,7 +1390,8 @@ public class MessagingController implements Runnable {
             public void run() {
                 // TODO IMAP
                 try {
-                    Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+                    Store localStore = Store.getInstance(
+                            account.getLocalStoreUri(), mApplication, null);
                     Folder localFolder = localStore.getFolder(account.getTrashFolderName());
                     localFolder.open(OpenMode.READ_WRITE);
                     Message[] messages = localFolder.getMessages(null);
@@ -1446,7 +1448,7 @@ public class MessagingController implements Runnable {
 
     public void saveDraft(final Account account, final Message message) {
         try {
-            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
+            Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication, null);
             LocalFolder localFolder =
                 (LocalFolder) localStore.getFolder(account.getDraftsFolderName());
             localFolder.open(OpenMode.READ_WRITE);
diff --git a/src/com/android/email/activity/AccountShortcutPicker.java b/src/com/android/email/activity/AccountShortcutPicker.java
index 554b69076..0e481f22e 100644
--- a/src/com/android/email/activity/AccountShortcutPicker.java
+++ b/src/com/android/email/activity/AccountShortcutPicker.java
@@ -124,8 +124,7 @@ public class AccountShortcutPicker extends ListActivity implements OnItemClickLi
             int unreadMessageCount = 0;
             try {
                 LocalStore localStore = (LocalStore) Store.getInstance(
-                        account.getLocalStoreUri(),
-                        getApplication());
+                        account.getLocalStoreUri(), getApplication(), null);
                 LocalFolder localFolder = (LocalFolder) localStore.getFolder(Email.INBOX);
                 if (localFolder.exists()) {
                     unreadMessageCount = localFolder.getUnreadMessageCount();
diff --git a/src/com/android/email/activity/Accounts.java b/src/com/android/email/activity/Accounts.java
index 20eedcb8b..9dc5d7ac0 100644
--- a/src/com/android/email/activity/Accounts.java
+++ b/src/com/android/email/activity/Accounts.java
@@ -174,11 +174,13 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli
                         // Delete Remote store at first.
                         Store.getInstance(
                                 mSelectedContextAccount.getStoreUri(),
-                                getApplication()).delete();
+                                getApplication(), 
+                                mSelectedContextAccount.getStoreCallbacks()).delete();
                         // If no error, then delete LocalStore
                         Store.getInstance(
                                 mSelectedContextAccount.getLocalStoreUri(),
-                                getApplication()).delete();
+                                getApplication(), 
+                                null).delete();
                     } catch (Exception e) {
                             // Ignore
                     }
@@ -299,7 +301,8 @@ public class Accounts extends ListActivity implements OnItemClickListener, OnCli
             try {
                 LocalStore localStore = (LocalStore) Store.getInstance(
                         account.getLocalStoreUri(),
-                        getApplication());
+                        getApplication(), 
+                        null);
                 LocalFolder localFolder = (LocalFolder) localStore.getFolder(Email.INBOX);
                 if (localFolder.exists()) {
                     unreadMessageCount = localFolder.getUnreadMessageCount();
diff --git a/src/com/android/email/activity/setup/AccountSettings.java b/src/com/android/email/activity/setup/AccountSettings.java
index 52536c706..32781b25c 100644
--- a/src/com/android/email/activity/setup/AccountSettings.java
+++ b/src/com/android/email/activity/setup/AccountSettings.java
@@ -195,7 +195,8 @@ public class AccountSettings extends PreferenceActivity {
 
     private void onIncomingSettings() {
         try {
-            Store store = Store.getInstance(mAccount.getStoreUri(), getApplication());
+            Store store = Store.getInstance(mAccount.getStoreUri(), getApplication(), 
+                    mAccount.getStoreCallbacks());
             if (store != null) {
                 Class setting = store.getSettingActivityClass();
                 if (setting != null) {
diff --git a/src/com/android/email/activity/setup/AccountSetupAccountType.java b/src/com/android/email/activity/setup/AccountSetupAccountType.java
index 58e910490..b9b9c623c 100644
--- a/src/com/android/email/activity/setup/AccountSetupAccountType.java
+++ b/src/com/android/email/activity/setup/AccountSetupAccountType.java
@@ -141,7 +141,7 @@ public class AccountSetupAccountType extends Activity implements OnClickListener
         try {
             URI uri = new URI(mAccount.getStoreUri());
             uri = new URI("eas", uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
-            Store store = Store.getInstance(uri.toString(), this);
+            Store store = Store.getInstance(uri.toString(), this, mAccount.getStoreCallbacks());
             return (store != null);
         } catch (URISyntaxException e) {
             return false;
diff --git a/src/com/android/email/activity/setup/AccountSetupCheckSettings.java b/src/com/android/email/activity/setup/AccountSetupCheckSettings.java
index a86ed8f32..829d65028 100644
--- a/src/com/android/email/activity/setup/AccountSetupCheckSettings.java
+++ b/src/com/android/email/activity/setup/AccountSetupCheckSettings.java
@@ -104,7 +104,8 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
                     }
                     if (mCheckIncoming) {
                         setMessage(R.string.account_setup_check_settings_check_incoming_msg);
-                        Store store = Store.getInstance(mAccount.getStoreUri(), getApplication());
+                        Store store = Store.getInstance(mAccount.getStoreUri(), getApplication(), 
+                                mAccount.getStoreCallbacks());
                         store.checkSettings();
                     }
                     if (mDestroyed) {
diff --git a/src/com/android/email/mail/Store.java b/src/com/android/email/mail/Store.java
index 3a5cf881c..a7c39c873 100644
--- a/src/com/android/email/mail/Store.java
+++ b/src/com/android/email/mail/Store.java
@@ -57,20 +57,22 @@ public abstract class Store {
      * Static named constructor.  It should be overrode by extending class.
      * Because this method will be called through reflection, it can not be protected. 
      */
-    public static Store newInstance(String uri, Context context)
+    public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks)
             throws MessagingException {
         throw new MessagingException("Store.newInstance: Unknown scheme in " + uri);
     }
 
-    private static Store instantiateStore(String className, String uri, Context context)
+    private static Store instantiateStore(String className, String uri, Context context, 
+            PersistentDataCallbacks callbacks)
         throws MessagingException {
         Object o = null;
         try {
             Class c = Class.forName(className);
             // and invoke "newInstance" class method and instantiate store object.
             java.lang.reflect.Method m =
-                c.getMethod("newInstance", String.class, Context.class);
-            o = m.invoke(null, uri, context);
+                c.getMethod("newInstance", String.class, Context.class, 
+                        PersistentDataCallbacks.class);
+            o = m.invoke(null, uri, context, callbacks);
         } catch (Exception e) {
             Log.d(Email.LOG_TAG, String.format(
                     "exception %s invoking %s.newInstance.(String, Context) method for %s",
@@ -149,13 +151,14 @@ public abstract class Store {
      * @return an initialized store of the appropriate class
      * @throws MessagingException
      */
-    public synchronized static Store getInstance(String uri, Context context)
+    public synchronized static Store getInstance(String uri, Context context, 
+            PersistentDataCallbacks callbacks)
         throws MessagingException {
         Store store = mStores.get(uri);
         if (store == null) {
             StoreInfo info = StoreInfo.getStoreInfo(uri, context);
             if (info != null) {
-                store = instantiateStore(info.mClassName, uri, context);
+                store = instantiateStore(info.mClassName, uri, context, callbacks);
             }
 
             if (store != null) {
@@ -212,4 +215,23 @@ public abstract class Store {
      */
     public void delete() throws MessagingException {
     }
+    
+    /**
+     * Callback interface by which a Store can read and write persistent data.
+     * TODO This needs to be made more generic & flexible
+     */
+    public interface PersistentDataCallbacks {
+        
+        /**
+         * Provides a small place for Stores to store persistent data.
+         * @param storeData Data to persist.  All data must be encoded into a string,
+         * so use base64 or some other encoding if necessary.
+         */
+        public void setPersistentString(String storeData);
+
+        /**
+         * @return the data saved by the Store, or null if never set.
+         */
+        public String getPersistentString();
+    }
 }
diff --git a/src/com/android/email/mail/exchange/ExchangeStoreExample.java b/src/com/android/email/mail/exchange/ExchangeStoreExample.java
index 444fb68b8..2b6af2d96 100644
--- a/src/com/android/email/mail/exchange/ExchangeStoreExample.java
+++ b/src/com/android/email/mail/exchange/ExchangeStoreExample.java
@@ -43,6 +43,7 @@ public class ExchangeStoreExample extends Store {
     
     private final Context mContext;
     private URI mUri;
+    private PersistentDataCallbacks mCallbacks;
 
     private final ExchangeTransportExample mTransport;
     private final HashMap mFolders = new HashMap();
@@ -52,9 +53,9 @@ public class ExchangeStoreExample extends Store {
     /**
      * Factory method.
      */
-    public static Store newInstance(String uri, Context context)
+    public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks)
     throws MessagingException {
-        return new ExchangeStoreExample(uri, context);
+        return new ExchangeStoreExample(uri, context, callbacks);
     }
 
     /**
@@ -64,13 +65,15 @@ public class ExchangeStoreExample extends Store {
      * @param application
      * @throws MessagingException
      */
-    private ExchangeStoreExample(String _uri, Context context) throws MessagingException {
+    private ExchangeStoreExample(String _uri, Context context, PersistentDataCallbacks callbacks)
+            throws MessagingException {
         mContext = context;
         try {
             mUri = new URI(_uri);
         } catch (URISyntaxException e) {
             throw new MessagingException("Invalid uri for ExchangeStoreExample");
         }
+        mCallbacks = callbacks;
 
         String scheme = mUri.getScheme();
         int connectionSecurity;
diff --git a/src/com/android/email/mail/store/ImapStore.java b/src/com/android/email/mail/store/ImapStore.java
index ad5844b08..2f924a5b6 100644
--- a/src/com/android/email/mail/store/ImapStore.java
+++ b/src/com/android/email/mail/store/ImapStore.java
@@ -17,7 +17,6 @@
 package com.android.email.mail.store;
 
 import com.android.email.Email;
-import com.android.email.PeekableInputStream;
 import com.android.email.Utility;
 import com.android.email.mail.AuthenticationFailedException;
 import com.android.email.mail.CertificateValidationException;
@@ -112,7 +111,8 @@ public class ImapStore extends Store {
     /**
      * Static named constructor.
      */
-    public static Store newInstance(String uri, Context context) throws MessagingException {
+    public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks)
+            throws MessagingException {
         return new ImapStore(uri);
     }
 
diff --git a/src/com/android/email/mail/store/Pop3Store.java b/src/com/android/email/mail/store/Pop3Store.java
index 47750a6d1..813879893 100644
--- a/src/com/android/email/mail/store/Pop3Store.java
+++ b/src/com/android/email/mail/store/Pop3Store.java
@@ -85,7 +85,8 @@ public class Pop3Store extends Store {
     /**
      * Static named constructor.
      */
-    public static Store newInstance(String uri, Context context) throws MessagingException {
+    public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks)
+            throws MessagingException {
         return new Pop3Store(uri);
     }
 
diff --git a/src/com/android/email/service/MailService.java b/src/com/android/email/service/MailService.java
index 7d1fb00a5..ad8f833e9 100644
--- a/src/com/android/email/service/MailService.java
+++ b/src/com/android/email/service/MailService.java
@@ -283,7 +283,8 @@ public class MailService extends Service {
         try {
             String storeUri = account.getStoreUri();
             if (storeUri != null) {
-                Store store = Store.getInstance(storeUri, this.getBaseContext());
+                Store store = Store.getInstance(storeUri, this.getBaseContext(), 
+                        account.getStoreCallbacks());
                 if (store != null) {
                     store.enablePushModeDelivery(enable);
                 }
diff --git a/tests/src/com/android/email/AccountUnitTests.java b/tests/src/com/android/email/AccountUnitTests.java
index 46ca501ef..4e58b13a8 100644
--- a/tests/src/com/android/email/AccountUnitTests.java
+++ b/tests/src/com/android/email/AccountUnitTests.java
@@ -16,6 +16,8 @@
 
 package com.android.email;
 
+import com.android.email.mail.Store;
+
 import android.content.SharedPreferences;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -135,6 +137,59 @@ public class AccountUnitTests extends AndroidTestCase {
         assertEquals(Account.DELETE_POLICY_ON_DELETE, storedPolicy);
     }
     
+    /**
+     * Test the new store persistent data code.
+     * 
+     * This test, and the underlying code, reflect the essential error in the Account class.  The
+     * account objects should have been singletons-per-account.  As it stands there are lots of
+     * them floating around, which is very expensive (we waste a lot of effort creating them)
+     * and forces slow sync hacks for dynamic data like the store's persistent data.
+     */
+    public void testStorePersistentData() {
+
+        final String TEST_STRING = "This is the store's persistent data.";
+        final String TEST_STRING_2 = "Rewrite the store data.";
+
+        // create a dummy account
+        createTestAccount();
+        
+        // confirm null on new account
+        assertNull(mAccount.getPersistentString());
+        
+        // test write/readback
+        mAccount.setPersistentString(TEST_STRING);
+        mAccount.save(mPreferences);
+        mAccount.refresh(mPreferences);
+        assertEquals(TEST_STRING, mAccount.getPersistentString());
+        
+        // test that the data is shared across multiple account instantiations
+        Account account2 = new Account(mPreferences, mUuid);
+        assertEquals(TEST_STRING, account2.getPersistentString());
+        mAccount.setPersistentString(TEST_STRING_2);
+        assertEquals(TEST_STRING_2, account2.getPersistentString());        
+    }
+    
+    /**
+     * Test the callbacks for setting & getting persistent data
+     */
+    public void testStorePersistentCallbacks() {
+
+        final String TEST_STRING = "This is the store's persistent data.";
+        final String TEST_STRING_2 = "Rewrite the store data.";
+
+        // create a dummy account
+        createTestAccount();
+        Store.PersistentDataCallbacks callbacks = mAccount.getStoreCallbacks();
+        
+        // push some data through the interfaces
+        assertNull(callbacks.getPersistentString());
+        callbacks.setPersistentString(TEST_STRING);
+        assertEquals(TEST_STRING, mAccount.getPersistentString());
+        
+        mAccount.setPersistentString(TEST_STRING_2);
+        assertEquals(TEST_STRING_2, callbacks.getPersistentString());
+   }
+    
     /**
      * Create a dummy account with minimal fields
      */
diff --git a/tests/src/com/android/email/mail/StoreTests.java b/tests/src/com/android/email/mail/StoreTests.java
index 9df89f4e0..ea951f8e0 100644
--- a/tests/src/com/android/email/mail/StoreTests.java
+++ b/tests/src/com/android/email/mail/StoreTests.java
@@ -38,7 +38,7 @@ public class StoreTests extends AndroidTestCase {
         assertFalse(info.mPushSupported);
         
         // This will throw MessagingException if the result would have been null
-        Store store = Store.getInstance(storeUri, getContext());
+        Store store = Store.getInstance(storeUri, getContext(), null);
     }
         
     /**
@@ -54,11 +54,12 @@ public class StoreTests extends AndroidTestCase {
         assertFalse(info.mPushSupported);
         
         // This will throw MessagingException if the result would have been null
-        Store store = Store.getInstance(storeUri, getContext());
+        Store store = Store.getInstance(storeUri, getContext(), null);
     }
         
     /**
      * Test StoreInfo & Store lookup for EAS accounts
+     * TODO: EAS store will probably require implementation of Store.PersistentDataCallbacks
      */
     public void testStoreLookupEAS() throws MessagingException {
         final String storeUri = "eas://user:password@server.com";
@@ -69,10 +70,10 @@ public class StoreTests extends AndroidTestCase {
             assertTrue(info.mPushSupported);
             
             // This will throw MessagingException if the result would have been null
-            Store store = Store.getInstance(storeUri, getContext());
+            Store store = Store.getInstance(storeUri, getContext(), null);
         } else {
             try {
-                Store store = Store.getInstance(storeUri, getContext());
+                Store store = Store.getInstance(storeUri, getContext(), null);
                 fail("MessagingException expected when EAS not supported");
             } catch (MessagingException me) {
                 // expected - fall through
@@ -89,7 +90,7 @@ public class StoreTests extends AndroidTestCase {
         assertNull(info);
 
         try {
-            Store store = Store.getInstance(storeUri, getContext());
+            Store store = Store.getInstance(storeUri, getContext(), null);
             fail("MessagingException expected from bogus URI scheme");
         } catch (MessagingException me) {
             // expected - fall through
diff --git a/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java b/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java
index 6f21723c0..ef0b2f6bc 100644
--- a/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java
+++ b/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java
@@ -50,7 +50,7 @@ public class ImapStoreUnitTests extends AndroidTestCase {
         
         // These are needed so we can get at the inner classes
         mStore = (ImapStore) ImapStore.newInstance("imap://user:password@server:999",
-                getContext());
+                getContext(), null);
         mFolder = (ImapStore.ImapFolder) mStore.getFolder("INBOX");
         
         // This is needed for parsing mime messages
diff --git a/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java b/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java
index 02af5f0dd..d05f3b331 100644
--- a/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java
+++ b/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java
@@ -57,7 +57,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
         
         // These are needed so we can get at the inner classes
         mStore = (Pop3Store) Pop3Store.newInstance("pop3://user:password@server:999",
-                getContext());
+                getContext(), null);
         mFolder = (Pop3Store.Pop3Folder) mStore.getFolder("INBOX");
         
         // This is needed for parsing mime messages