Merge "Change ActionBar to allow reading real accounts."
diff --git a/res/layout/conversation_list.xml b/res/layout/conversation_list.xml
index 8ec0a67..c42311a 100644
--- a/res/layout/conversation_list.xml
+++ b/res/layout/conversation_list.xml
@@ -44,12 +44,12 @@
        android:fadingEdge="none"
        android:layout_alignParentTop="true"/>
 
-    <com.android.mail.UndoBarView
+    <com.android.mail.ui.UndoBarView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:visibility="gone"
         android:id="@+id/undo_view"
         android:layout_alignParentBottom="true">
         <include layout="@layout/undo_bar"/>
-    </com.android.mail.UndoBarView>
+    </com.android.mail.ui.UndoBarView>
 </RelativeLayout>
diff --git a/src/com/android/mail/AccountSpinnerAdapter.java b/src/com/android/mail/AccountSpinnerAdapter.java
index 018b8fb..7ad3e9c 100644
--- a/src/com/android/mail/AccountSpinnerAdapter.java
+++ b/src/com/android/mail/AccountSpinnerAdapter.java
@@ -17,8 +17,11 @@
 
 package com.android.mail;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.Cursor;
 import android.database.DataSetObserver;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -27,6 +30,10 @@
 import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 
+import com.android.mail.providers.AccountCacheProvider;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.LogUtils;
+
 /**
  * An adapter to return the list of accounts and labels for the Account Spinner.
  * This class gets a merge cursor and returns views that are appropriate for the
@@ -35,10 +42,37 @@
  *
  */
 public class AccountSpinnerAdapter implements SpinnerAdapter,ListAdapter {
-    private String mLabels[];
-    private Integer mUnreadCounts[];
-    private LayoutInflater mInflater;
+    private final LayoutInflater mInflater;
+    /**
+     * The current account being viewed
+     */
     private String mCurrentAccount;
+    private final ContentResolver mResolver;
+
+    /**
+     * Total number of accounts.
+     */
+    private int numAccounts;
+
+    /**
+     *  Cursor into the accounts database
+     */
+    private final Cursor mAccountCursor;
+
+    /**
+     *  The name of the account is the 2nd column in UIProvider.ACCOUNTS_PROJECTION
+     */
+    static final int NAME_COLUMN = 2;
+    /**
+     * Fake labels for now
+     */
+    private final String[] mFolders;
+    /**
+     *  Fake unread counts
+     */
+    private final int[] mUnreadCounts = {
+            0, 2, 42
+    };
 
     /**
      * When the user selects the spinner, a dropdown list of objects is shown. Each item in the
@@ -50,7 +84,7 @@
     }
 
     /**
-     * The first dropdown item is a header.
+     * After the accounts, the dropdown item is a header.
      */
     private static class HeaderHolder {
         TextView account;
@@ -65,29 +99,54 @@
         TextView unread_count;
     }
 
+    private static final int TYPE_ACCOUNT = 0;
+    private static final int TYPE_HEADER = 1;
+    private static final int TYPE_FOLDER = 2;
+
+    private int getType(int position) {
+        // First the accounts
+        if (position < numAccounts) {
+            return TYPE_ACCOUNT;
+        }
+        // Then the header
+        if (position == numAccounts) {
+            return TYPE_HEADER;
+        }
+        // Finally, the recent folders.
+        return TYPE_FOLDER;
+    }
+
     public AccountSpinnerAdapter(Context context) {
         mInflater = LayoutInflater.from(context);
-        // Fake data.
-        mLabels = new String[3];
-        mLabels[0] = "Inbox";
-        mLabels[1] = "Outbox";
-        mLabels[2] = "Drafts";
-        mUnreadCounts = new Integer[3];
-        mUnreadCounts[0] = 13;
-        mUnreadCounts[1] = 1;
-        mUnreadCounts[2] = 0;
-        mCurrentAccount = "test@android.com";
+
+        // Get the data from the system Accounts
+        mResolver = context.getContentResolver();
+        mAccountCursor = mResolver.query(AccountCacheProvider.getAccountsUri(),
+                UIProvider.ACCOUNTS_PROJECTION, null, null, null);
+        numAccounts = mAccountCursor.getCount();
+        // Fake folder information
+        mFolders = new String[3];
+        mFolders[0] = "Drafts -fake";
+        mFolders[1] = "Sent -fake";
+        mFolders[1] = "Starred -fake";
     }
 
     @Override
     public int getCount() {
-        // All the labels, plus one header.
-        return mLabels.length + 1;
+        // All the accounts, plus one header, and optionally some labels
+        return numAccounts + 1 + mFolders.length;
     }
 
     @Override
     public Object getItem(int position) {
-        return mLabels[position - 1];
+        switch (getType(position)){
+            case TYPE_ACCOUNT:
+                return getAccount(position);
+            case TYPE_HEADER:
+                return "account spinner header";
+            default:
+                return mFolders[position - numAccounts - 1];
+        }
     }
 
     @Override
@@ -98,23 +157,43 @@
 
     @Override
     public int getItemViewType(int position) {
-        if (position == 0) {
-            return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
+        // We have three different types of views: accounts, header and folders. The constants for
+        // account and folder are arbitrary, and we choose numbers 0 and 1.
+        switch (getType(position)) {
+            case TYPE_ACCOUNT:
+                return 0;
+            case TYPE_HEADER:
+                return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
+            default:
+                return 1;
         }
-        // If there are a variety of views, this returns the kind of view that the position
-        // represents. So if there are two views, and you have view types 0, and 1, then this
-        // method returns which view type the 'position' represents.
-
-        // Since we have a single view type, we always return zero.
-        return 0;
     }
 
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
-        if (position == 0) {
-            // We can never select the header, and we want the default view to be the Inbox.
-            return getView(1, convertView, parent);
+        String folderName = "";
+        String accountName = "";
+        int unreadCount = 0;
+        switch (getType(position)) {
+            case TYPE_ACCOUNT:
+                // The default Inbox for the given account
+                accountName = getAccount(position);
+                folderName = "Inbox";
+                break;
+            case TYPE_HEADER:
+                // We can never select the header, and we want the default view to be the Inbox.
+                accountName = getAccount(0);
+                folderName = "Inbox";
+                break;
+            default:
+                // Change the name of the current label
+                final int offset = position - numAccounts - 1;
+                accountName = mCurrentAccount;
+                folderName = mFolders[offset];
+                unreadCount = mUnreadCounts[offset];
+                break;
         }
+
         // Return a view with the label on the first line and the account name on the second.
         ViewHolder holder;
         if (convertView == null) {
@@ -130,10 +209,14 @@
         } else {
             holder = (ViewHolder) convertView.getTag();
         }
-        holder.label.setText(mLabels[position - 1]);
-        holder.account.setText(mCurrentAccount);
-        holder.unread_count.setText(mUnreadCounts[position - 1].toString());
-        if (mUnreadCounts[position - 1] == 0) {
+        // TODO(viki): Fill with a real label here.
+        // TODO(viki): Also rename label to folder.
+        holder.label.setText(folderName);
+        holder.account.setText(accountName);
+        // Fake unread counts for now.
+        holder.unread_count.setText(String.valueOf(unreadCount));
+        // Keep the unread count logic here for the future.
+        if (unreadCount == 0) {
             holder.unread_count.setVisibility(View.GONE);
         } else {
             holder.unread_count.setVisibility(View.VISIBLE);
@@ -141,87 +224,114 @@
         return convertView;
     }
 
+
     @Override
     public int getViewTypeCount() {
-        // One view, and one header
-        return 2;
+        // Two views, and one header
+        return 3;
     }
 
+
     @Override
     public boolean hasStableIds() {
-        // The data generated is static for now, the IDs are stable. However, in the future this
-        // won't be the case
-        return true;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        // Will always contain something.
+        // The account manager could add new accounts, so don't claim that the IDs are stable.
         return false;
     }
 
+
+    @Override
+    public boolean isEmpty() {
+        // No item will be empty.
+        return false;
+    }
+
+
     @Override
     public void registerDataSetObserver(DataSetObserver observer) {
-        // Don't do anything for now, since the data is mocked.
+        // Don't do anything for now. In the future, we will want to re-read the account
+        // information. In particular: recalculating the total number of accounts.
     }
 
+
     @Override
     public void unregisterDataSetObserver(DataSetObserver observer) {
-        // Don't do anything for now, since the data is mocked.
+        // Don't do anything for now.
     }
 
+
     @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        // At the top, we show a header. This is a special view.
-        if (position == 0) {
-            HeaderHolder holder;
-            if (convertView == null ||
-                    !(convertView.getTag() instanceof HeaderHolder)) {
-                convertView =
-                        mInflater.inflate(R.layout.account_switch_spinner_dropdown_header, null);
-                holder = new HeaderHolder();
-                holder.account =
-                        (TextView) convertView.findViewById(R.id.account_spinner_header_account);
-                convertView.setTag(holder);
-            } else {
-                holder = (HeaderHolder) convertView.getTag();
-            }
-            holder.account.setText(mCurrentAccount);
-            return convertView;
+        String textLabel = "";
+        int unreadCount = 0;
+        switch (getType(position)) {
+            case TYPE_HEADER:
+                HeaderHolder header;
+                if (convertView == null || !(convertView.getTag() instanceof HeaderHolder)) {
+                    convertView = mInflater.inflate(
+                            R.layout.account_switch_spinner_dropdown_header, null);
+                    header = new HeaderHolder();
+                    header.account = (TextView) convertView.findViewById(
+                            R.id.account_spinner_header_account);
+                    convertView.setTag(header);
+                } else {
+                    header = (HeaderHolder) convertView.getTag();
+                }
+                header.account.setText(mCurrentAccount);
+                return convertView;
+            case TYPE_ACCOUNT:
+                textLabel = getAccount(position);
+                break;
+            case TYPE_FOLDER:
+                final int offset = position - numAccounts - 1;
+                textLabel = mFolders[offset];
+                unreadCount = mUnreadCounts[offset];
+                break;
         }
 
-        DropdownHolder holder;
-        if (convertView == null ||
-                !(convertView.getTag() instanceof DropdownHolder)) {
+        DropdownHolder dropdown;
+        if (convertView == null || !(convertView.getTag() instanceof DropdownHolder)) {
             convertView = mInflater.inflate(R.layout.account_switch_spinner_dropdown_item, null);
-            holder = new DropdownHolder();
-            holder.label = (TextView) convertView.findViewById(R.id.account_spinner_accountname);
-            holder.unread_count =
+            dropdown = new DropdownHolder();
+            dropdown.label = (TextView) convertView.findViewById(R.id.account_spinner_accountname);
+            dropdown.unread_count =
                     (TextView) convertView.findViewById(R.id.account_spinner_unread_count);
-            convertView.setTag(holder);
+            convertView.setTag(dropdown);
         } else {
-            holder = (DropdownHolder) convertView.getTag();
+            dropdown = (DropdownHolder) convertView.getTag();
         }
 
-        holder.label.setText(mLabels[position - 1]);
-        holder.unread_count.setText(mUnreadCounts[position - 1].toString());
-        if (mUnreadCounts[position - 1] == 0) {
-            holder.unread_count.setVisibility(View.GONE);
+        dropdown.label.setText(textLabel);
+        dropdown.unread_count.setText(String.valueOf(unreadCount));
+        if (unreadCount == 0) {
+            dropdown.unread_count.setVisibility(View.GONE);
         } else {
-            holder.unread_count.setVisibility(View.VISIBLE);
+            dropdown.unread_count.setVisibility(View.VISIBLE);
         }
         return convertView;
+
     }
 
+    /**
+     * Returns the name of the label at the given position in the spinner.
+     * @param position
+     * @return
+     */
+    private String getAccount(int position) {
+        mAccountCursor.moveToPosition(position);
+        final int accountNameCol = mAccountCursor.getColumnIndex(UIProvider.AccountColumns.NAME);
+        return mAccountCursor.getString(accountNameCol);
+    }
+
+
     @Override
     public boolean isEnabled(int position) {
         // Don't want the user selecting the header.
-        return position != 0;
+        return (getType(position) != TYPE_HEADER);
     }
 
     @Override
     public boolean areAllItemsEnabled() {
-        // The header is the only non-enabled item.
+        // The header is not enabled, so return false here.
         return false;
     }
 }
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 92b87cf..3d35b8b 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -67,6 +67,9 @@
     protected Account mAccount;
     protected ActionBarView mActionBarView;
     protected final RestrictedActivity mActivity;
+    /**
+     * The currently selected conversations, if any.
+     */
     private ConversationSelectionSet mBatchConversations = new ConversationSelectionSet();
     protected final Context mContext;
     protected ConversationListContext mConversationListContext;
@@ -123,8 +126,7 @@
 
     @Override
     public ConversationSelectionSet getBatchConversations() {
-        // TODO(viki): Auto-generated method stub
-        return null;
+        return mBatchConversations;
     }
 
     @Override
@@ -250,9 +252,6 @@
 
     @Override
     public boolean onCreate(Bundle savedState) {
-        // Request opaque actionbar
-        mActivity.getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
-
         // Initialize the action bar view.
         initCustomActionBarView();
 
@@ -334,8 +333,9 @@
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
-        // TODO(viki): Auto-generated method stub
-
+        if (mConversationListContext != null) {
+            outState.putBundle(SAVED_LIST_CONTEXT, mConversationListContext.toBundle());
+        }
     }
 
     @Override
@@ -425,7 +425,10 @@
      */
     protected void restoreListContext(Bundle savedState) {
         // TODO(viki): Auto-generated method stub
-
+        Bundle listContextBundle = savedState.getBundle(SAVED_LIST_CONTEXT);
+        if (listContextBundle != null) {
+            mConversationListContext = ConversationListContext.forBundle(listContextBundle);
+        }
     }
 
     /**
@@ -456,6 +459,9 @@
     protected void restoreState(Bundle savedState) {
         if (savedState != null) {
             restoreListContext(savedState);
+            // Restore the list context
+            restoreListContext(savedState);
+
             // Attach the menu handler here.
 
             // Restore the view mode
@@ -463,6 +469,8 @@
         } else {
             final Intent intent = mActivity.getIntent();
             //  TODO(viki): Show the list context from Intent
+            mConversationListContext = ConversationListContext.forIntent(
+                    mContext, mAccount, intent);
             // Instead of this, switch to the conversation list mode and have that do the right
             // things automatically.
             // showConversationList(ConversationListContext.forIntent(mContext, mAccount, intent));
diff --git a/src/com/android/mail/ui/ActionBarView.java b/src/com/android/mail/ui/ActionBarView.java
index be76edc..a1899a7 100644
--- a/src/com/android/mail/ui/ActionBarView.java
+++ b/src/com/android/mail/ui/ActionBarView.java
@@ -92,8 +92,10 @@
 
     /**
      * Change the mode of the actionbar.
+     * <p> TODO(viki): Why does the Actionbar have its own mode? The actionbar should use the view
+     * mode, just like everyone else.
      * @param mode
-     * @return true if the change in mode was successful
+     * @return true if the change in mode was successful.
      */
     boolean setMode(Mode mode);
 
diff --git a/src/com/android/mail/ui/ControllableActivity.java b/src/com/android/mail/ui/ControllableActivity.java
index 067080a..15e9e41 100644
--- a/src/com/android/mail/ui/ControllableActivity.java
+++ b/src/com/android/mail/ui/ControllableActivity.java
@@ -17,6 +17,7 @@
 
 package com.android.mail.ui;
 
+import com.android.mail.browse.ConversationItemView.StarHandler;
 import com.android.mail.ui.ViewMode.ModeChangeListener;
 
 
@@ -24,7 +25,8 @@
  * A controllable activity is an Activity that has a Controller attached. This activity must be
  * able to attach the various view fragments and delegate the method calls between them.
  */
-public interface ControllableActivity extends HelpCallback, RestrictedActivity {
+public interface ControllableActivity extends HelpCallback, RestrictedActivity,
+        ConversationListCallbacks, StarHandler {
     /**
      * Attaches the conversation list fragment to the activity controller. This callback is
      * currently required because the Activity Controller directly calls methods on the conversation
diff --git a/src/com/android/mail/ui/MailActivity.java b/src/com/android/mail/ui/MailActivity.java
index 528b503..4260dcd 100644
--- a/src/com/android/mail/ui/MailActivity.java
+++ b/src/com/android/mail/ui/MailActivity.java
@@ -106,6 +106,11 @@
     }
 
     @Override
+    public void onConversationSelected(int position) {
+        // Do nothing for now. Allow the mController to handle these changes.
+    }
+
+    @Override
     public void onCreate(Bundle savedState) {
         super.onCreate(savedState);
 
@@ -210,6 +215,12 @@
     }
 
     @Override
+    public void toggleStar(boolean toggleOn, long conversationId, long maxMessageId) {
+        // TODO(viki): Auto-generated method stub
+        // Do nothing for now, and let the mController handle this too.
+    }
+
+    @Override
     public void unsetViewModeListener(ModeChangeListener listener) {
         mViewMode.removeListener(listener);
     }
diff --git a/src/com/android/mail/ui/OnePaneController.java b/src/com/android/mail/ui/OnePaneController.java
index 07625c2..005f047 100644
--- a/src/com/android/mail/ui/OnePaneController.java
+++ b/src/com/android/mail/ui/OnePaneController.java
@@ -34,8 +34,6 @@
 // Called OnePaneActivityController in Gmail.
 public final class OnePaneController extends AbstractActivityController {
 
-    private ConversationListContext mConversationListContext;
-
     /**
      * @param activity
      * @param viewMode