Handle up and back behavior for the one pane/ two pane folder lists.

Adding hierarchies makes this more complex.

Change-Id: I6f4946c351c53dbfda880780fe804d8e57c4a494
diff --git a/res/drawable-sw600dp/folder_item.xml b/res/drawable-sw600dp/folder_item.xml
deleted file mode 100644
index 7632b33..0000000
--- a/res/drawable-sw600dp/folder_item.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2011 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/list_pressed_holo" />
-    <item android:state_activated="true" android:drawable="@drawable/list_activated_holo" />
-    <item android:drawable="@android:color/transparent" />
-</selector>
diff --git a/res/drawable/folder_item.xml b/res/drawable/folder_item.xml
index cc362ed..7632b33 100644
--- a/res/drawable/folder_item.xml
+++ b/res/drawable/folder_item.xml
@@ -17,6 +17,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/list_selected_holo" />
+    <item android:state_pressed="true" android:drawable="@drawable/list_pressed_holo" />
+    <item android:state_activated="true" android:drawable="@drawable/list_activated_holo" />
     <item android:drawable="@android:color/transparent" />
 </selector>
diff --git a/src/com/android/mail/providers/Folder.java b/src/com/android/mail/providers/Folder.java
index dcc41c2..ee9abe4 100644
--- a/src/com/android/mail/providers/Folder.java
+++ b/src/com/android/mail/providers/Folder.java
@@ -138,10 +138,17 @@
     public Uri loadMoreUri;
 
     /**
+     * Parent folder of this folder, or null if there is none. This is set as
+     * part of the execution of the application and not obtained or stored via
+     * the provider.
+     */
+    public Folder parent;
+
+    /**
      * Total number of members that comprise an instance of a folder. This is
      * the number of members that need to be serialized or parceled.
      */
-    private static final int NUMBER_MEMBERS = UIProvider.FOLDERS_PROJECTION.length;
+    private static final int NUMBER_MEMBERS = UIProvider.FOLDERS_PROJECTION.length + 1;
 
     /**
      * Used only for debugging.
@@ -190,6 +197,7 @@
         bgColor = in.readString();
         fgColor = in.readString();
         loadMoreUri = in.readParcelable(null);
+        parent = in.readParcelable(null);
      }
 
     public Folder(Cursor cursor) {
@@ -218,6 +226,7 @@
         fgColor = cursor.getString(UIProvider.FOLDER_FG_COLOR_COLUMN);
         String loadMore = cursor.getString(UIProvider.FOLDER_LOAD_MORE_URI_COLUMN);
         loadMoreUri = !TextUtils.isEmpty(loadMore) ? Uri.parse(loadMore) : null;
+        parent = null;
     }
 
     @Override
@@ -241,6 +250,7 @@
         dest.writeString(bgColor);
         dest.writeString(fgColor);
         dest.writeParcelable(loadMoreUri, 0);
+        dest.writeParcelable(parent, 0);
     }
 
     /**
@@ -265,7 +275,8 @@
         out.append(iconResId).append(FOLDER_COMPONENT_SEPARATOR);
         out.append(bgColor == null ? "" : bgColor).append(FOLDER_COMPONENT_SEPARATOR);
         out.append(fgColor == null? "" : fgColor).append(FOLDER_COMPONENT_SEPARATOR);
-        out.append(loadMoreUri);
+        out.append(loadMoreUri).append(FOLDER_COMPONENT_SEPARATOR);
+        out.append(""); //set parent to empty
         return out.toString();
     }
 
@@ -331,6 +342,7 @@
         fgColor = folderMembers[16];
         String loadMore = folderMembers[17];
         loadMoreUri = !TextUtils.isEmpty(loadMore) ? Uri.parse(loadMore) : null;
+        parent = null;
     }
 
     /**
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 626ffea..55f2893 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -116,7 +116,7 @@
     protected static final String TAG_FOLDER_LIST = "tag-folder-list";
 
     protected Account mAccount;
-    protected Folder mFolder;
+    private Folder mFolder;
     protected ActionBarView mActionBarView;
     protected final RestrictedActivity mActivity;
     protected final Context mContext;
@@ -443,7 +443,7 @@
     @Override
     public void onFolderChanged(Folder folder) {
         if (folder != null && !folder.equals(mFolder)) {
-            setFolder(folder);
+            updateFolder(folder);
             mConvListContext = ConversationListContext.forFolder(mContext, mAccount, mFolder);
             showConversationList(mConvListContext);
 
@@ -478,21 +478,21 @@
     }
 
     /** Set the current folder */
-    private void setFolder(Folder folder) {
+    private void updateFolder(Folder folder) {
         // Start watching folder for sync status.
         if (folder != null && !folder.equals(mFolder)) {
             LogUtils.d(LOG_TAG, "AbstractActivityController.setFolder(%s)", folder.name);
-            final boolean folderWasNull = (mFolder == null);
             final LoaderManager lm = mActivity.getLoaderManager();
             mActionBarView.setRefreshInProgress(false);
-            mFolder = folder;
+            setFolder(folder);
             mActionBarView.setFolder(mFolder);
 
             // Only when we switch from one folder to another do we want to restart the
             // folder and conversation list loaders (to trigger onCreateLoader).
             // The first time this runs when the activity is [re-]initialized, we want to re-use the
             // previous loader's instance and data upon configuration change (e.g. rotation).
-            if (folderWasNull) {
+            // If there was not already an instance of the loader, init it.
+            if (lm.getLoader(LOADER_FOLDER_CURSOR) == null) {
                 lm.initLoader(LOADER_FOLDER_CURSOR, null, this);
                 lm.initLoader(LOADER_CONVERSATION_LIST, null, mListCursorCallbacks);
             } else {
@@ -504,6 +504,17 @@
         }
     }
 
+    /**
+     * Set the folder that is used for all current operations, including what
+     * conversation list to show (if applicable), what item to select in the
+     * FolderListFragment.
+     *
+     * @param folder
+     */
+    public void setFolder(Folder folder) {
+        mFolder = folder;
+    }
+
     @Override
     public Folder getFolder() {
         return mFolder;
@@ -1404,7 +1415,7 @@
             case LOADER_SEARCH:
                 data.moveToFirst();
                 Folder search = new Folder(data);
-                setFolder(search);
+                updateFolder(search);
                 mConvListContext = ConversationListContext.forSearchQuery(mAccount, mFolder,
                         mActivity.getIntent()
                                 .getStringExtra(UIProvider.SearchQueryParameters.QUERY));
diff --git a/src/com/android/mail/ui/FolderListFragment.java b/src/com/android/mail/ui/FolderListFragment.java
index a3ceb43..b960daa 100644
--- a/src/com/android/mail/ui/FolderListFragment.java
+++ b/src/com/android/mail/ui/FolderListFragment.java
@@ -164,6 +164,11 @@
         } else {
             folder = new Folder((Cursor) item);
         }
+        // Since we may be looking at hierarchical views, if we can determine
+        // the parent of the folder we have tapped, set it here.
+        // If we are looking at the folder we are already viewing, don't update
+        // its parent!
+        folder.parent = folder.equals(mParentFolder) ? null : mParentFolder;
         // Go to the conversation list for this folder.
         mListener.onFolderSelected(folder, mParentFolder != null);
     }
@@ -284,4 +289,12 @@
     public interface FolderListSelectionListener {
         public void onFolderSelected(Folder folder, boolean viewingChildren);
     }
+
+    /**
+     * Get whether the FolderListFragment is currently showing the hierarchy
+     * under a single parent.
+     */
+    public boolean showingHierarchy() {
+        return mParentFolder != null;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/mail/ui/OnePaneController.java b/src/com/android/mail/ui/OnePaneController.java
index bb22c3a..dc7a684 100644
--- a/src/com/android/mail/ui/OnePaneController.java
+++ b/src/com/android/mail/ui/OnePaneController.java
@@ -221,7 +221,7 @@
         }
 
         // TODO: improve this transition
-        mPagerController.show(mAccount, mFolder, conversation);
+        mPagerController.show(mAccount, getFolder(), conversation);
         onConversationVisibilityChanged(true);
         resetActionBarIcon();
 
@@ -248,6 +248,9 @@
             LogUtils.e(LOG_TAG, "Null account in showFolderList");
             return;
         }
+        // Null out the currently selected folder; we have nothing selected the
+        // first time the user enters the folder list
+        setFolder(null);
         mViewMode.enterFolderListMode();
         enableCabMode();
         mLastFolderListTransactionId = replaceFragment(
@@ -294,17 +297,43 @@
     public boolean onBackPressed() {
         final int mode = mViewMode.getMode();
         if (mode == ViewMode.FOLDER_LIST) {
-            mLastFolderListTransactionId = INVALID_ID;
-            transitionToInbox();
+            if (getFolderListFragment().showingHierarchy()) {
+                // If we are showing the folder list and the user is exploring
+                // the children of a single parent folder,
+                // back should display the parent folder's parent and siblings.
+                if (getFolder() != null && getFolder().parent != null) {
+                    onFolderSelected(getFolder().parent, true);
+                } else {
+                    // If there was no parent, this must have been a top level
+                    // folder, so just show the top level folder list.
+                    showFolderList();
+                }
+            } else {
+                // We are at the topmost list of folders; just go back to
+                // whatever conv list we were viewing before.
+                mLastFolderListTransactionId = INVALID_ID;
+                transitionToInbox();
+            }
         } else if (mode == ViewMode.SEARCH_RESULTS_LIST) {
             mActivity.finish();
         } else if (mode == ViewMode.CONVERSATION_LIST && !inInbox(mAccount, mConvListContext)) {
-            if (isTransactionIdValid(mLastFolderListTransactionId)) {
-                // Go back to previous folder list.
+            if (mLastFolderListTransactionId != INVALID_ID) {
+                // If the user got here by navigating via the folder list, back
+                // should bring them back to the folder list.
                 mViewMode.enterFolderListMode();
+                if (getFolder() != null && getFolder().parent != null) {
+                    // If there was a parent folder, show the parent and
+                    // siblings of the current folder for which we are viewing
+                    // the conversation list.
+                    setFolder(getFolder().parent);
+                } else {
+                    // Otherwise, clear the selected folder and go back to whatever the last
+                    // folder list displayed was.
+                    setFolder(null);
+                }
                 mActivity.getFragmentManager().popBackStack(mLastFolderListTransactionId, 0);
             } else {
-                // Go back to Inbox.
+                mLastFolderListTransactionId = INVALID_ID;
                 transitionToInbox();
             }
         } else if (mode == ViewMode.CONVERSATION || mode == ViewMode.SEARCH_RESULTS_CONVERSATION) {
@@ -332,22 +361,23 @@
 
     @Override
     public void onFolderSelected(Folder folder, boolean childView) {
-        super.onFolderSelected(folder, childView);
-        if (!childView && folder.hasChildren) {
+        if (folder.hasChildren && !getFolderListFragment().showingHierarchy()) {
+            setFolder(folder);
             // Replace this fragment with a new FolderListFragment
-            // showing this folder's children if we are not already looking
-            // at the child view for this folder.
+            // showing this folder's children if we are not already
+            // looking at the child view for this folder.
             mLastFolderListTransactionId = replaceFragment(
                     FolderListFragment.newInstance(folder, folder.childFoldersListUri),
                     FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_FOLDER_LIST);
             return;
-        }
-        if (mViewMode.getMode() == ViewMode.FOLDER_LIST && folder != null
-                && folder.equals(mFolder)) {
-            // if we are in folder list when we select a new folder,
-            // and it is the same as the existing folder, clear the previous
-            // folder setting so that the folder will be re-loaded/ shown.
-            mFolder = null;
+        } else {
+            // We are looking at the child folders of this folder, so just
+            // open the conv list for this folder.
+            // We set the folder to null to clear the selected folder and
+            // make sure that everything gets updated in case we were previously
+            // viewing the child folders or conversation list for the selected folder.
+            setFolder(null);
+            super.onFolderChanged(folder);
         }
     }
 
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 5c10842..3ba5acb 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -226,7 +226,7 @@
         } else {
             mViewMode.enterConversationMode();
         }
-        mPagerController.show(mAccount, mFolder, conversation);
+        mPagerController.show(mAccount, getFolder(), conversation);
         final ConversationListFragment convList = getConversationListFragment();
         if (convList != null) {
             LogUtils.d(LOG_TAG, "showConversation: Selecting position %d.", conversation.position);
@@ -288,9 +288,7 @@
         } else if (mode == ViewMode.SEARCH_RESULTS_LIST) {
             mActivity.finish();
         } else if (mode == ViewMode.CONVERSATION_LIST) {
-            // This case can only happen if the user is looking at child folders.
-            createFolderListFragment(null, mAccount.folderListUri);
-            loadAccountInbox();
+            popView(true);
         }
         return true;
     }
@@ -309,9 +307,11 @@
      * @param preventClose Whether to prevent closing the app if the stack is empty.
      */
     protected void popView(boolean preventClose) {
-        // If the user is in search query entry mode, or the user is viewing search results, exit
+        // If the user is in search query entry mode, or the user is viewing
+        // search results, exit
         // the mode.
         int mode = mViewMode.getMode();
+        FolderListFragment folderListFragment = getFolderListFragment();
         if (mode == ViewMode.SEARCH_RESULTS_LIST) {
             mActivity.finish();
         } else if (mode == ViewMode.CONVERSATION) {
@@ -320,8 +320,20 @@
         } else if (mode == ViewMode.SEARCH_RESULTS_CONVERSATION) {
             mViewMode.enterSearchResultsListMode();
         } else {
-            // There is nothing else to pop off the stack.
-            if (!preventClose) {
+            if (mode == ViewMode.CONVERSATION_LIST && getFolderListFragment().showingHierarchy()) {
+                // If the user navigated via the left folders list into a child folder,
+                // back should take the user up to the parent folder's conversation list.
+                if (getFolder().parent != null) {
+                    onFolderSelected(getFolder().parent, true);
+                } else  {
+                    // Show inbox; we are at the top of the hierarchy we were
+                    // showing, and it doesn't have a parent, so we must want to
+                    // the basic account folder list.
+                    createFolderListFragment(null, mAccount.folderListUri);
+                    loadAccountInbox();
+                }
+            } else if (!preventClose) {
+                // There is nothing else to pop off the stack.
                 mActivity.finish();
             }
         }