Get the Account through a listener

Every object that needs an account gets it from the ActivityController
by registering for future updates.  This should reduce some of the
headaches with account objects going out of sync.

Fix b/6969950

Change-Id: I0449b482ecd84bdb947304db5f0504c77dd1dc7a
diff --git a/src/com/android/mail/AccountSpinnerAdapter.java b/src/com/android/mail/AccountSpinnerAdapter.java
index d41dda2..f5bd75a 100644
--- a/src/com/android/mail/AccountSpinnerAdapter.java
+++ b/src/com/android/mail/AccountSpinnerAdapter.java
@@ -27,10 +27,11 @@
 import android.widget.TextView;
 
 import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountObserver;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.FolderWatcher;
+import com.android.mail.ui.ControllableActivity;
 import com.android.mail.ui.RecentFolderList;
-import com.android.mail.ui.RestrictedActivity;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.Utils;
@@ -91,6 +92,28 @@
 
     private static final String LOG_TAG = LogTag.getLogTag();
 
+    final AccountObserver mAccountObserver = new AccountObserver() {
+        @Override
+        public void onChanged(Account newAccount){
+            // If the account is missing or we have no accounts array, we cannot
+            // proceed.
+            if (newAccount == null) {
+                return;
+            }
+            if (newAccount.uri.equals(getCurrentAccountUri())) {
+                // The current account matches what is being given, get out.
+                return;
+            }
+            mCurrentAccount = newAccount;
+            final int pos = Account.findPosition(mAllAccounts, newAccount.uri);
+            LogUtils.d(LOG_TAG, "setCurrentAccount: mCurrentAccountPos = %d", pos);
+            if (pos >= 0) {
+                requestRecentFoldersAndRedraw();
+            }
+            notifyDataSetChanged();
+        }
+    };
+
     /**
      * There can be three types of views: Accounts (test@android.com, fifi@example.com), folders
      * (Inbox, Outbox) or header and footer. This method returns the type of account at given
@@ -143,7 +166,7 @@
      * @param recentFolders
      * @param showAllFolders
      */
-    public AccountSpinnerAdapter(RestrictedActivity activity, Context context,
+    public AccountSpinnerAdapter(ControllableActivity activity, Context context,
             RecentFolderList recentFolders, boolean showAllFolders) {
         mContext = context;
         mInflater = LayoutInflater.from(context);
@@ -152,6 +175,7 @@
         // Owned by the AccountSpinnerAdapter since nobody else needed it. Move to controller if
         // required.
         mFolderWatcher = new FolderWatcher(activity);
+        mCurrentAccount = mAccountObserver.initialize(activity.getAccountController());
     }
 
 
@@ -189,30 +213,6 @@
         return false;
     }
 
-    /**
-     * Set the current account.
-     * @param account
-     * @return if changed.
-     */
-    public boolean setCurrentAccount(Account account) {
-        // If the account is missing or we have no accounts array, we cannot
-        // proceed.
-        if (account == null) {
-            return false;
-        }
-        if (account.uri.equals(getCurrentAccountUri())) {
-            // The current account matches what is being given, get out.
-            return false;
-        }
-        mCurrentAccount = account;
-        final int pos = Account.findPosition(mAllAccounts, account.uri);
-        LogUtils.d(LOG_TAG, "setCurrentAccount: mCurrentAccountPos = %d", pos);
-        if (pos >= 0) {
-            requestRecentFoldersAndRedraw();
-        }
-        return true;
-    }
-
     @Override
     public int getCount() {
         // If the recent folders are visible, then one header, recent folders, plus one if the
@@ -467,4 +467,11 @@
             mRecentFoldersVisible = true;
         }
     }
+
+    /**
+     * Destroys the spinner adapter
+     */
+    public void destroy() {
+        mAccountObserver.unregisterAndDestroy();
+    }
 }
diff --git a/src/com/android/mail/browse/SelectedConversationsActionMenu.java b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
index 4dfbae4..d92e895 100644
--- a/src/com/android/mail/browse/SelectedConversationsActionMenu.java
+++ b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
@@ -29,6 +29,7 @@
 
 import com.android.mail.R;
 import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountObserver;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.MailAppProvider;
@@ -85,18 +86,24 @@
     /** Object that can update conversation state on our behalf. */
     private final ConversationUpdater mUpdater;
 
-    private final Account mAccount;
+    private Account mAccount;
 
     private final Folder mFolder;
 
     private final SwipeableListView mListView;
+    private final AccountObserver mAccountObserver = new AccountObserver() {
+        @Override
+        public void onChanged(Account newAccount) {
+            mAccount = newAccount;
+        }
+    };
 
-    public SelectedConversationsActionMenu(RestrictedActivity activity,
-            ConversationSelectionSet selectionSet, Account account,
+    public SelectedConversationsActionMenu(ControllableActivity activity,
+            ConversationSelectionSet selectionSet,
             Folder folder, SwipeableListView list) {
         mActivity = activity;
         mSelectionSet = selectionSet;
-        mAccount = account;
+        mAccount = mAccountObserver.initialize(activity.getAccountController());
         mFolder = folder;
         mListView = list;
 
@@ -222,7 +229,7 @@
 
     private void performDestructiveAction(final int action) {
         final DestructiveAction destructiveAction = mUpdater.getDeferredBatchAction(action);
-        final Settings settings = mActivity.getSettings();
+        final Settings settings = mAccount.settings;
         final Collection<Conversation> conversations = mSelectionSet.values();
         final boolean showDialog = (settings != null
                 && (action == R.id.delete) ? settings.confirmDelete : settings.confirmArchive);
@@ -491,6 +498,7 @@
         mSelectionSet.removeObserver(this);
         clearSelection();
         mUpdater.refreshConversationList();
+        mAccountObserver.unregisterAndDestroy();
     }
 
     /**
diff --git a/src/com/android/mail/providers/AccountObserver.java b/src/com/android/mail/providers/AccountObserver.java
new file mode 100644
index 0000000..6a1accf
--- /dev/null
+++ b/src/com/android/mail/providers/AccountObserver.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ * Licensed to 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.mail.providers;
+
+import android.database.DataSetObserver;
+
+import com.android.mail.ui.AccountController;
+import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
+
+/**
+ * A simple extension of {@link DataSetObserver} to provide the updated account in
+ * {@link #onChanged(Account)} when the account changes. Initializing the object registers with
+ * the observer with the {@link AccountController} provided. The object will then begin to
+ * receive {@link #onChanged(Account)} till {@link #unregisterAndDestroy()} is called.
+ * <p>
+ * To implement an {@link AccountObserver}, you need to implement the {@link #onChanged(Account)}
+ * method.
+ */
+public abstract class AccountObserver extends DataSetObserver {
+    /**
+     * The AccountController that the observer is registered with.
+     */
+    private AccountController mController;
+
+    private static final String LOG_TAG = LogTag.getLogTag();
+
+    /**
+     * The no-argument constructor leaves the object unusable till
+     * {@link #initialize(AccountController)} is called.
+     */
+    public AccountObserver () {
+    }
+
+    /**
+     * Initializes an {@link AccountObserver} object that receives a call to
+     * {@link #onChanged(Account)} when the controller changes the account.
+     *
+     * @param controller
+     */
+    public Account initialize(AccountController controller) {
+        if (controller == null) {
+            LogUtils.wtf(LOG_TAG, "AccountObserver initialized with null controller!");
+        }
+        mController = controller;
+        mController.registerAccountObserver(this);
+        return mController.getAccount();
+    }
+
+    @Override
+    public final void onChanged() {
+        onChanged(mController.getAccount());
+    }
+
+    /**
+     * Callback invoked when the account object is changed.  Since {@link Account} objects are
+     * immutable, updates can be received on changes to individual settings (sync on/off)
+     * in addition to changes of accounts: alice@example.com -> bob@example.com.
+     * The updated account is passed as the argument.
+     * @param newAccount
+     */
+    public abstract void onChanged(Account newAccount);
+
+    /**
+     * Return the most current account.
+     * @return
+     */
+    public final Account getAccount() {
+        return mController.getAccount();
+    }
+
+    /**
+     * Unregisters for account changes and makes the object unusable.
+     */
+    public void unregisterAndDestroy() {
+        mController.unregisterAccountObserver(this);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/mail/providers/Settings.java b/src/com/android/mail/providers/Settings.java
index a6708f1..4fc40a6 100644
--- a/src/com/android/mail/providers/Settings.java
+++ b/src/com/android/mail/providers/Settings.java
@@ -41,18 +41,6 @@
  * Model to hold Settings for an account.
  */
 public class Settings implements Parcelable {
-    /**
-     * Interface to listen to settings changes. You need to register with the
-     * {@link com.android.mail.ui.ActivityController} for observing changes to settings.
-     */
-    public interface ChangeListener {
-        /**
-         * Method that is called when settings are changed.
-         * @param updatedSettings the updated settings.
-         */
-        public void onSettingsChanged(Settings updatedSettings);
-    }
-
     private static final String LOG_TAG = LogTag.getLogTag();
 
     static final Settings EMPTY_SETTINGS = new Settings();
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 5f1038d..00f136f 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -133,7 +133,7 @@
     protected Account mAccount;
     protected Folder mFolder;
     protected MailActionBarView mActionBarView;
-    protected final RestrictedActivity mActivity;
+    protected final ControllableActivity mActivity;
     protected final Context mContext;
     private final FragmentManager mFragmentManager;
     protected final RecentFolderList mRecentFolderList;
@@ -164,23 +164,37 @@
         public void registerObserver(DataSetObserver observer) {
             final int count = mObservers.size();
             super.registerObserver(observer);
-            LogUtils.d(LOG_TAG, "IN AAC.registerListObserver: %s before=%d after=%d", observer,
+            LogUtils.d(LOG_TAG, "IN AAC.register(List)Observer: %s before=%d after=%d", observer,
                     count, mObservers.size());
         }
         @Override
         public void unregisterObserver(DataSetObserver observer) {
             final int count = mObservers.size();
             super.unregisterObserver(observer);
-            LogUtils.d(LOG_TAG, "IN AAC.unregisterListObserver: %s before=%d after=%d", observer,
+            LogUtils.d(LOG_TAG, "IN AAC.unregister(List)Observer: %s before=%d after=%d", observer,
                     count, mObservers.size());
         }
     };
 
-    private boolean mIsConversationListScrolling = false;
     private RefreshTimerTask mConversationListRefreshTask;
 
-    /** Listeners that are interested in changes to current account settings. */
-    private final ArrayList<Settings.ChangeListener> mSettingsListeners = Lists.newArrayList();
+    /** Listeners that are interested in changes to the current account. */
+    private final DataSetObservable mAccountObservers = new DataSetObservable() {
+        @Override
+        public void registerObserver(DataSetObserver observer) {
+            final int count = mObservers.size();
+            super.registerObserver(observer);
+            LogUtils.d(LOG_TAG, "IN AAC.register(Account)Observer: %s before=%d after=%d",
+                    observer, count, mObservers.size());
+        }
+        @Override
+        public void unregisterObserver(DataSetObserver observer) {
+            final int count = mObservers.size();
+            super.unregisterObserver(observer);
+            LogUtils.d(LOG_TAG, "IN AAC.unregister(Account)Observer: %s before=%d after=%d",
+                    observer, count, mObservers.size());
+        }
+    };
 
     /**
      * Selected conversations, if any.
@@ -365,11 +379,9 @@
                 mAccount.uri);
         cancelRefreshTask();
         updateSettings();
-        mActionBarView.setAccount(mAccount);
         if (shouldReloadInbox) {
             loadAccountInbox();
         }
-        mRecentFolderList.setCurrentAccount(account);
         restartOptionalLoader(LOADER_RECENT_FOLDERS);
         mActivity.invalidateOptionsMenu();
         disableNotificationsOnAccountChange(mAccount);
@@ -403,7 +415,7 @@
      * @param settings
      */
     public void updateSettings() {
-        notifySettingsChanged();
+        mAccountObservers.notifyChanged();
         resetActionBarIcon();
         mActivity.invalidateOptionsMenu();
         // If the user was viewing the default Inbox here, and the new setting contains a different
@@ -411,47 +423,28 @@
         // current folder.
     }
 
+    /**
+     * Adds a listener interested in change in the current account. If a class is storing a
+     * reference to the current account, it should listen on changes, so it can receive updates to
+     * settings. Must happen in the UI thread.
+     */
     @Override
-    public Settings getSettings() {
-        return mAccount.settings;
+    public void registerAccountObserver(DataSetObserver obs) {
+        mAccountObservers.registerObserver(obs);
     }
 
     /**
-     * Adds a listener interested in change in settings. If a class is storing a reference to
-     * Settings, it should listen on changes, so it can receive updates to settings.
+     * Removes a listener from receiving current account changes.
      * Must happen in the UI thread.
      */
-    public void addSettingsListener(Settings.ChangeListener listener) {
-        mSettingsListeners.add(listener);
+    @Override
+    public void unregisterAccountObserver(DataSetObserver obs) {
+        mAccountObservers.unregisterObserver(obs);
     }
 
-    /**
-     * Removes a listener from receiving settings changes.
-     * Must happen in the UI thread.
-     */
-    public void removeSettingsListener(Settings.ChangeListener listener) {
-        mSettingsListeners.remove(listener);
-    }
-
-    /**
-     * Method that lets the settings listeners know when the settings got changed.
-     */
-    private void notifySettingsChanged() {
-        final Settings updatedSettings = mAccount.settings;
-        // Copy the list of current listeners so that
-        final ArrayList<Settings.ChangeListener> allListeners =
-                new ArrayList<Settings.ChangeListener>(mSettingsListeners);
-        for (Settings.ChangeListener listener : allListeners) {
-            if (listener != null) {
-                listener.onSettingsChanged(updatedSettings);
-            }
-        }
-        // And we know that the ConversationListFragment is interested in changes to settings,
-        // though it hasn't registered itself with us.
-        final ConversationListFragment convList = getConversationListFragment();
-        if (convList != null) {
-            convList.onSettingsChanged(updatedSettings);
-        }
+    @Override
+    public Account getAccount() {
+        return mAccount;
     }
 
     private void fetchSearchFolder(Intent intent) {
@@ -604,6 +597,7 @@
         mActivity.setDefaultKeyMode(Activity.DEFAULT_KEYS_SHORTCUT);
         mResolver = mActivity.getContentResolver();
         mNewEmailReceiver = new SuppressNotificationReceiver();
+        mRecentFolderList.initialize(mActivity);
 
         // All the individual UI components listen for ViewMode changes. This
         // simplifies the amount of logic in the AbstractActivityController, but increases the
@@ -1065,7 +1059,7 @@
         // unregister the ViewPager's observer on the conversation cursor
         mPagerController.onDestroy();
         mActionBarView.onDestroy();
-
+        mRecentFolderList.destroy();
         mDestroyed = true;
     }
 
@@ -1120,12 +1114,11 @@
         }
         LogUtils.d(LOG_TAG, "AbstractActivityController.setAccount(): account = %s", account.uri);
         mAccount = account;
-        mActionBarView.setAccount(mAccount);
         if (account.settings == null) {
             LogUtils.w(LOG_TAG, new Error(), "AAC ignoring account with null settings.");
             return;
         }
-        notifySettingsChanged();
+        mAccountObservers.notifyChanged();
     }
 
     /**
@@ -1240,7 +1233,6 @@
                 setAccount((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
                 mActivity.invalidateOptionsMenu();
                 restartOptionalLoader(LOADER_RECENT_FOLDERS);
-                mRecentFolderList.setCurrentAccount(mAccount);
                 fetchSearchFolder(intent);
             } else {
                 LogUtils.e(LOG_TAG, "Missing account extra from search intent.  Finishing");
@@ -1625,7 +1617,7 @@
 
                         // Only notify about a settings change if something differs
                         if (!Objects.equal(mAccount.settings, previousSettings)) {
-                            notifySettingsChanged();
+                            mAccountObservers.notifyChanged();
                         }
 
                         // Got an update for the current account
@@ -1986,7 +1978,7 @@
         if (convList == null) {
             return;
         }
-        mCabActionMenu = new SelectedConversationsActionMenu(mActivity, set, mAccount, mFolder,
+        mCabActionMenu = new SelectedConversationsActionMenu(mActivity, set, mFolder,
                 (SwipeableListView) convList.getListView());
         enableCabMode();
     }
diff --git a/src/com/android/mail/ui/AccountController.java b/src/com/android/mail/ui/AccountController.java
new file mode 100644
index 0000000..226c9a3
--- /dev/null
+++ b/src/com/android/mail/ui/AccountController.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ * Licensed to 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.mail.ui;
+
+import android.database.DataSetObserver;
+
+import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountObserver;
+
+/**
+ * This class consolidates account-specific actions taken by a mail activity.
+ */
+public interface AccountController {
+    /**
+     * Registers to receive changes to the current account, and obtain the current account.
+     */
+    void registerAccountObserver(DataSetObserver observer);
+
+    /**
+     * Removes a listener from receiving current account changes.
+     */
+    void unregisterAccountObserver(DataSetObserver observer);
+
+    /**
+     * Returns the current account in use by the controller. Instead of calling this method,
+     * consider registering for account changes using
+     * {@link AccountObserver#initialize(AccountController)}, which not only provides the current
+     * account, but also updates to the account, in case of settings changes.
+     */
+    Account getAccount();
+}
diff --git a/src/com/android/mail/ui/ActivityController.java b/src/com/android/mail/ui/ActivityController.java
index f8df26c..814d146 100644
--- a/src/com/android/mail/ui/ActivityController.java
+++ b/src/com/android/mail/ui/ActivityController.java
@@ -33,7 +33,6 @@
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
-import com.android.mail.providers.Settings;
 import com.android.mail.ui.ViewMode.ModeChangeListener;
 
 /**
@@ -46,7 +45,7 @@
         FolderChangeListener, AccountChangeListener, LoaderManager.LoaderCallbacks<Cursor>,
         ConversationSetObserver, ConversationListener,
         FolderListFragment.FolderListSelectionListener, HelpCallback, UndoListener,
-        ConversationUpdater, ErrorListener, FolderController {
+        ConversationUpdater, ErrorListener, FolderController, AccountController {
 
     // As far as possible, the methods here that correspond to Activity lifecycle have the same name
     // as their counterpart in the Activity lifecycle.
@@ -245,12 +244,6 @@
     void onTouchEvent(MotionEvent event);
 
     /**
-     * Return the settings currently being used by this activity.
-     * @return
-     */
-    Settings getSettings();
-
-    /**
      * Returns whether the first conversation in the conversation list should be
      * automatically selected and shown.
      */
diff --git a/src/com/android/mail/ui/AnimatedAdapter.java b/src/com/android/mail/ui/AnimatedAdapter.java
index 4dffe8a..fb16677 100644
--- a/src/com/android/mail/ui/AnimatedAdapter.java
+++ b/src/com/android/mail/ui/AnimatedAdapter.java
@@ -33,9 +33,9 @@
 import com.android.mail.browse.ConversationItemView;
 import com.android.mail.browse.SwipeableConversationItemView;
 import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountObserver;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
-import com.android.mail.providers.Settings;
 import com.android.mail.providers.UIProvider;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
@@ -46,7 +46,7 @@
 import java.util.HashSet;
 
 public class AnimatedAdapter extends SimpleCursorAdapter implements
-        android.animation.Animator.AnimatorListener, Settings.ChangeListener {
+        android.animation.Animator.AnimatorListener {
     private static final String LAST_DELETING_ITEMS = "last_deleting_items";
     private static final String LEAVE_BEHIND_ITEM = "leave_behind_item";
     private final static int TYPE_VIEW_CONVERSATION = 0;
@@ -63,7 +63,7 @@
     private final HashMap<Long, LeaveBehindItem> mFadeLeaveBehindItems =
             new HashMap<Long, LeaveBehindItem>();
     /** The current account */
-    private final Account mAccount;
+    private Account mAccount;
     private final Context mContext;
     private final ConversationSelectionSet mBatchConversations;
     /**
@@ -91,30 +91,46 @@
     private boolean mShowFooter;
     private Folder mFolder;
     private final SwipeableListView mListView;
-    private Settings mCachedSettings;
-    private final boolean mSwipeEnabled;
+    private boolean mSwipeEnabled;
     private LeaveBehindItem mLeaveBehindItem;
     /** True if priority inbox markers are enabled, false otherwise. */
-    private final boolean mPriorityMarkersEnabled;
+    private boolean mPriorityMarkersEnabled;
     private ControllableActivity mActivity;
+    private final AccountObserver mAccountListener = new AccountObserver() {
+        @Override
+        public void onChanged(Account newAccount) {
+            setAccount(newAccount);
+            notifyDataSetChanged();
+        }
+    };
+
+    private final void setAccount(Account newAccount) {
+        mAccount = newAccount;
+        mPriorityMarkersEnabled = mAccount.settings.priorityArrowsEnabled;
+        mSwipeEnabled = mAccount.supportsCapability(UIProvider.AccountCapabilities.UNDO);
+    }
+
     /**
      * Used only for debugging.
      */
     private static final String LOG_TAG = LogTag.getLogTag();
 
     public AnimatedAdapter(Context context, int textViewResourceId, ConversationCursor cursor,
-            ConversationSelectionSet batch, Account account, Settings settings,
+            ConversationSelectionSet batch,
             ControllableActivity activity, SwipeableListView listView) {
         super(context, textViewResourceId, cursor, UIProvider.CONVERSATION_PROJECTION, null, 0);
         mContext = context;
         mBatchConversations = batch;
-        mAccount = account;
+        setAccount(mAccountListener.initialize(activity.getAccountController()));
         mActivity = activity;
         mShowFooter = false;
         mListView = listView;
-        mCachedSettings = settings;
-        mSwipeEnabled = account.supportsCapability(UIProvider.AccountCapabilities.UNDO);
-        mPriorityMarkersEnabled = account.settings.priorityArrowsEnabled;
+    }
+
+    public final void destroy() {
+        // Set a null cursor in the adapter
+        swapCursor(null);
+        mAccountListener.unregisterAndDestroy();
     }
 
     @Override
@@ -156,7 +172,7 @@
             return;
         }
         ((SwipeableConversationItemView) view).bind(cursor, mActivity, mBatchConversations, mFolder,
-                mCachedSettings != null ? mCachedSettings.hideCheckboxes : false,
+                mAccount != null ? mAccount.settings.hideCheckboxes : false,
                         mSwipeEnabled, mPriorityMarkersEnabled, this);
     }
 
@@ -391,7 +407,7 @@
         SwipeableConversationItemView view = (SwipeableConversationItemView) super.getView(
                 position, null, parent);
         view.bind(conversation, mActivity, mBatchConversations, mFolder,
-                mCachedSettings != null ? mCachedSettings.hideCheckboxes : false, mSwipeEnabled,
+                mAccount != null ? mAccount.settings.hideCheckboxes : false, mSwipeEnabled,
                 mPriorityMarkersEnabled, this);
         mAnimatingViews.put(conversation.id, view);
         return view;
@@ -565,16 +581,6 @@
         }
     }
 
-    /**
-     * Callback invoked when settings for the current account have been changed.
-     * @param updatedSettings
-     */
-    @Override
-    public void onSettingsChanged(Settings updatedSettings) {
-        mCachedSettings = updatedSettings;
-        notifyDataSetChanged();
-    }
-
     public void onSaveInstanceState(Bundle outState) {
         int[] lastDeleting = new int[mLastDeletingItems.size()];
         for (int i = 0; i < lastDeleting.length; i++) {
diff --git a/src/com/android/mail/ui/ControllableActivity.java b/src/com/android/mail/ui/ControllableActivity.java
index bbb185d..66fbe36 100644
--- a/src/com/android/mail/ui/ControllableActivity.java
+++ b/src/com/android/mail/ui/ControllableActivity.java
@@ -98,5 +98,15 @@
 
     ErrorListener getErrorListener();
 
+    /**
+     * Returns the {@link FolderController} object associated with this activity, if any.
+     * @return
+     */
     FolderController getFolderController();
+
+    /**
+     * Returns the {@link AccountController} object associated with this activity, if any.
+     * @return
+     */
+    AccountController getAccountController();
 }
diff --git a/src/com/android/mail/ui/ConversationListFragment.java b/src/com/android/mail/ui/ConversationListFragment.java
index 0ce8898..c70439b 100644
--- a/src/com/android/mail/ui/ConversationListFragment.java
+++ b/src/com/android/mail/ui/ConversationListFragment.java
@@ -40,6 +40,7 @@
 import com.android.mail.browse.ConversationItemView;
 import com.android.mail.browse.ConversationListFooterView;
 import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountObserver;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.Settings;
@@ -59,8 +60,7 @@
  * The conversation list UI component.
  */
 public final class ConversationListFragment extends ListFragment implements
-        OnItemLongClickListener, ModeChangeListener, SwipeCompleteListener,
-        Settings.ChangeListener {
+        OnItemLongClickListener, ModeChangeListener, SwipeCompleteListener {
     // Keys used to pass data to {@link ConversationListFragment}.
     private static final String CONVERSATION_LIST_KEY = "conversation-list";
     // Key used to keep track of the scroll state of the list.
@@ -118,6 +118,13 @@
     private DataSetObserver mConversationListStatusObserver;
 
     private ConversationSelectionSet mSelectedSet;
+    private final AccountObserver mAccountObserver = new AccountObserver() {
+        @Override
+        public void onChanged(Account newAccount) {
+            mAccount = newAccount;
+            setSwipeAction();
+        }
+    };
 
     /**
      * Constructor needs to be public to handle orientation changes and activity lifecycle events.
@@ -222,6 +229,9 @@
                     "create it. Cannot proceed.");
         }
         mActivity = (ControllableActivity) activity;
+        // Since we now have a controllable activity, load the account from it, and register for
+        // future account changes.
+        mAccount = mAccountObserver.initialize(mActivity.getAccountController());
         mCallbacks = mActivity.getListHandler();
         mErrorListener = mActivity.getErrorListener();
         // Start off with the current state of the folder being viewed.
@@ -230,8 +240,7 @@
                 null);
         mFooterView.setFragmentManager(getFragmentManager());
         mListAdapter = new AnimatedAdapter(mActivity.getApplicationContext(), -1,
-                getConversationListCursor(), mActivity.getSelectedSet(), mAccount,
-                mActivity.getSettings(), mActivity, mListView);
+                getConversationListCursor(), mActivity.getSelectedSet(), mActivity, mListView);
         mListAdapter.addFooter(mFooterView);
         mListView.setAdapter(mListAdapter);
         mSelectedSet = mActivity.getSelectedSet();
@@ -328,8 +337,8 @@
     public void onDestroyView() {
         mListSavedState = mListView.onSaveInstanceState();
 
-        // Set a null cursor in the dapter, and clear the list's adapter
-        mListAdapter.swapCursor(null);
+        // Clear the list's adapter
+        mListAdapter.destroy();
         mListView.setAdapter(null);
 
         mActivity.unsetViewModeListener(this);
@@ -345,6 +354,7 @@
                     mConversationListStatusObserver);
             mConversationListStatusObserver = null;
         }
+        mAccountObserver.unregisterAndDestroy();
         super.onDestroyView();
     }
 
@@ -537,7 +547,7 @@
     }
 
     private void setSwipeAction() {
-        int swipeSetting = Settings.getSwipeSetting(mActivity.getSettings());
+        int swipeSetting = Settings.getSwipeSetting(mAccount.settings);
         if (swipeSetting == Swipe.DISABLED
                 || !mAccount.supportsCapability(AccountCapabilities.UNDO)
                 || (mFolder != null && mFolder.isTrash())) {
@@ -607,11 +617,4 @@
         }
     }
 
-    @Override
-    public void onSettingsChanged(Settings updatedSettings) {
-        if (mListAdapter != null) {
-            mListAdapter.onSettingsChanged(updatedSettings);
-        }
-        setSwipeAction();
-    }
 }
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index b49558d..e2a9890 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -68,6 +68,7 @@
 import com.android.mail.browse.SuperCollapsedBlock;
 import com.android.mail.browse.WebViewContextMenu;
 import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountObserver;
 import com.android.mail.providers.Address;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
@@ -138,6 +139,13 @@
 
     private MenuItem mChangeFoldersMenuItem;
 
+    private final AccountObserver mAccountObserver = new AccountObserver() {
+        @Override
+        public void onChanged(Account newAccount) {
+            mAccount = newAccount;
+        }
+    };
+
     /**
      * Folder is used to help determine valid menu actions for this conversation.
      */
@@ -233,7 +241,7 @@
             return;
         }
         mTemplates = new HtmlConversationTemplates(mContext);
-
+        mAccount = mAccountObserver.initialize(mActivity.getAccountController());
         mAdapter = new ConversationViewAdapter(mActivity.getActivityContext(), mAccount,
                 getLoaderManager(), this, mContactLoaderCallbacks, this, this, mAddressCache);
         mConversationContainer.setOverlayAdapter(mAdapter);
@@ -360,6 +368,7 @@
             mMarkReadObserver = null;
         }
         mViewsCreated = false;
+        mAccountObserver.unregisterAndDestroy();
     }
 
     @Override
@@ -534,7 +543,7 @@
         boolean allowNetworkImages = false;
 
         // TODO: re-use any existing adapter item state (expanded, details expanded, show pics)
-        final Settings settings = mActivity.getSettings();
+        final Settings settings = mAccount.settings;
         if (settings != null) {
             mAdapter.setDefaultReplyAll(settings.replyBehavior ==
                     UIProvider.DefaultReplyBehavior.REPLY_ALL);
diff --git a/src/com/android/mail/ui/FolderSelectionActivity.java b/src/com/android/mail/ui/FolderSelectionActivity.java
index 61531a5..40e997b 100644
--- a/src/com/android/mail/ui/FolderSelectionActivity.java
+++ b/src/com/android/mail/ui/FolderSelectionActivity.java
@@ -36,7 +36,6 @@
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
-import com.android.mail.providers.Settings;
 import com.android.mail.ui.FolderListFragment.FolderListSelectionListener;
 import com.android.mail.ui.ViewMode.ModeChangeListener;
 import com.android.mail.utils.LogTag;
@@ -242,11 +241,6 @@
     }
 
     @Override
-    public Settings getSettings() {
-        return null;
-    }
-
-    @Override
     public boolean onSearchRequested(String query) {
         return false;
     }
@@ -336,4 +330,10 @@
     @Override
     public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
     }
+
+    @Override
+    public AccountController getAccountController() {
+        // Unsupported
+        return null;
+    }
 }
diff --git a/src/com/android/mail/ui/MailActionBarView.java b/src/com/android/mail/ui/MailActionBarView.java
index d7ff9b1..879456b 100644
--- a/src/com/android/mail/ui/MailActionBarView.java
+++ b/src/com/android/mail/ui/MailActionBarView.java
@@ -38,17 +38,15 @@
 import android.widget.TextView;
 import android.widget.SearchView.OnQueryTextListener;
 import android.widget.SearchView.OnSuggestionListener;
-import android.widget.Toast;
-
 import com.android.mail.AccountSpinnerAdapter;
 import com.android.mail.R;
 import com.android.mail.browse.SnippetTextView;
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Folder;
+import com.android.mail.providers.AccountObserver;
 import com.android.mail.providers.SearchRecentSuggestionsProvider;
 import com.android.mail.providers.UIProvider.AccountCapabilities;
 import com.android.mail.providers.UIProvider.FolderCapabilities;
-import com.android.mail.providers.UIProvider.LastSyncResult;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.Utils;
@@ -63,7 +61,7 @@
         ViewMode.ModeChangeListener, OnQueryTextListener, OnSuggestionListener,
         MenuItem.OnActionExpandListener, SubjectDisplayChanger {
     protected ActionBar mActionBar;
-    protected RestrictedActivity mActivity;
+    protected ControllableActivity mActivity;
     protected ActivityController mController;
     private View mFolderView;
     /**
@@ -108,6 +106,16 @@
     private TextView mFolderAccountName;
     private DataSetObserver mFolderObserver;
 
+    private final AccountObserver mAccountObserver = new AccountObserver() {
+        @Override
+        public void onChanged(Account newAccount) {
+            mAccount = newAccount;
+            if (mFolderAccountName != null) {
+                mFolderAccountName.setText(mAccount.name);
+            }
+        }
+    };
+
     public MailActionBarView(Context context) {
         this(context, null);
     }
@@ -216,7 +224,7 @@
     public void handleSaveInstanceState(Bundle outState) {
     }
 
-    public void initialize(RestrictedActivity activity, ActivityController callback,
+    public void initialize(ControllableActivity activity, ActivityController callback,
             ViewMode viewMode, ActionBar actionBar, RecentFolderList recentFolders) {
         mActionBar = actionBar;
         mController = callback;
@@ -226,6 +234,7 @@
         // We don't want to include the "Show all folders" menu item on tablet devices
         final boolean showAllFolders = !Utils.useTabletUI(getContext());
         mSpinner = new AccountSpinnerAdapter(activity, getContext(), recentFolders, showAllFolders);
+        mAccount = mAccountObserver.initialize(activity.getAccountController());
     }
 
     /**
@@ -268,17 +277,6 @@
         mFolder = folder;
     }
 
-    /**
-     * Called by the owner of the ActionBar to set the
-     * account that is currently being displayed.
-     */
-    public void setAccount(Account account) {
-        mAccount = account;
-        mSpinner.setCurrentAccount(account);
-        mSpinner.notifyDataSetChanged();
-        mFolderAccountName.setText(mAccount.name);
-    }
-
     @Override
     public boolean onNavigationItemSelected(int position, long id) {
         LogUtils.d(LOG_TAG, "onNavigationItemSelected(%d, %d) called", position, id);
@@ -323,6 +321,8 @@
             mController.unregisterFolderObserver(mFolderObserver);
             mFolderObserver = null;
         }
+        mSpinner.destroy();
+        mAccountObserver.unregisterAndDestroy();
     }
 
     @Override
diff --git a/src/com/android/mail/ui/MailActivity.java b/src/com/android/mail/ui/MailActivity.java
index b84a373..50923d6 100644
--- a/src/com/android/mail/ui/MailActivity.java
+++ b/src/com/android/mail/ui/MailActivity.java
@@ -33,6 +33,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import com.android.mail.providers.Account;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.Settings;
@@ -49,7 +50,6 @@
  * conversation list or a conversation view).
  */
 public class MailActivity extends AbstractMailActivity implements ControllableActivity {
-    // TODO(viki) This class lacks: Conversation Position tracking
     // TODO(viki) This class lacks: What's New dialog
     // TODO(viki) This class lacks: Sync Window Upgrade dialog
 
@@ -339,11 +339,6 @@
     }
 
     @Override
-    public Settings getSettings() {
-        return mController.getSettings();
-    }
-
-    @Override
     public boolean shouldShowFirstConversation() {
         return mController.shouldShowFirstConversation();
     }
@@ -418,4 +413,9 @@
     public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
         mController.onAnimationEnd(animatedAdapter);
     }
+
+    @Override
+    public AccountController getAccountController() {
+        return mController;
+    }
 }
diff --git a/src/com/android/mail/ui/RecentFolderList.java b/src/com/android/mail/ui/RecentFolderList.java
index 1719351..1114387 100644
--- a/src/com/android/mail/ui/RecentFolderList.java
+++ b/src/com/android/mail/ui/RecentFolderList.java
@@ -23,6 +23,7 @@
 import android.os.AsyncTask;
 
 import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountObserver;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.Settings;
 import com.android.mail.utils.LogUtils;
@@ -61,6 +62,13 @@
      */
     private final static int MAX_EXCLUDED_FOLDERS = 2;
 
+    private final AccountObserver mAccountObserver = new AccountObserver() {
+        @Override
+        public void onChanged(Account newAccount) {
+            setCurrentAccount(newAccount);
+        }
+    };
+
     /**
      * Compare based on alphanumeric name of the folder, ignoring case.
      */
@@ -74,8 +82,13 @@
      * Class to store the recent folder list asynchronously.
      */
     private class StoreRecent extends AsyncTask<Void, Void, Void> {
-        final Account mAccount;
-        final Folder mFolder;
+        /**
+         * Copy {@link RecentFolderList#mAccount} in case the account changes between when the
+         * AsyncTask is created and when it is executed.
+         */
+        @SuppressWarnings("hiding")
+        private final Account mAccount;
+        private final Folder mFolder;
 
         /**
          * Create a new asynchronous task to store the recent folder list. Both the account
@@ -107,7 +120,7 @@
     /**
      * Create a Recent Folder List from the given account. This will query the UIProvider to
      * retrieve the RecentFolderList from persistent storage (if any).
-     * @param account
+     * @param context
      */
     public RecentFolderList(Context context) {
         mFolderCache = new LruCache<String, Folder>(MAX_RECENT_FOLDERS);
@@ -115,12 +128,20 @@
     }
 
     /**
+     * Initialize the {@link RecentFolderList} with a controllable activity.
+     * @param activity
+     */
+    public void initialize(ControllableActivity activity){
+        setCurrentAccount(mAccountObserver.initialize(activity.getAccountController()));
+    }
+
+    /**
      * Change the current account. When a cursor over the recent folders for this account is
      * available, the client <b>must</b> call {@link #loadFromUiProvider(Cursor)} with the updated
      * cursor. Till then, the recent account list will be empty.
      * @param account the new current account
      */
-    public void setCurrentAccount(Account account) {
+    private void setCurrentAccount(Account account) {
         mAccount = account;
         mFolderCache.clear();
     }
@@ -193,4 +214,11 @@
         }
         return recentFolders;
     }
+
+    /**
+     * Destroys this instance. The object is unusable after this has been called.
+     */
+    public void destroy() {
+        mAccountObserver.unregisterAndDestroy();
+    }
 }
diff --git a/src/com/android/mail/ui/RestrictedActivity.java b/src/com/android/mail/ui/RestrictedActivity.java
index 915d900..cf16660 100644
--- a/src/com/android/mail/ui/RestrictedActivity.java
+++ b/src/com/android/mail/ui/RestrictedActivity.java
@@ -33,7 +33,7 @@
 import android.view.View;
 import android.view.Window;
 
-import com.android.mail.providers.Settings;
+// Should not rely on any mail-specific packages.
 
 /**
  * {@link RestrictedActivity} gives access to a subset of {@link Activity} methods. These methods
@@ -186,12 +186,6 @@
     Context getActivityContext();
 
     /**
-     * Return the settings currently being used by this activity.
-     * @return
-     */
-    Settings getSettings();
-
-    /**
      * @see Activity#onOptionsItemSelected(MenuItem)
      */
     boolean onOptionsItemSelected(MenuItem item);