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();
}
}