Start tying in fragments to Viewmode changes
Change-Id: I09eaeb0c338ab3205e0887a99b9684b4ac0549ca
diff --git a/res/menu/conversation_list_menu.xml b/res/menu/conversation_list_menu.xml
index 44221ab..329b11d 100644
--- a/res/menu/conversation_list_menu.xml
+++ b/res/menu/conversation_list_menu.xml
@@ -17,45 +17,52 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
+ <!-- Always available -->
<item android:id="@+id/compose"
android:title="@string/menu_compose"
android:showAsAction="always"
android:icon="@drawable/ic_menu_compose_normal_holo_light"
android:alphabeticShortcut="@string/trigger_compose_char" />
- <item android:id="@+id/search"
- android:title="@string/menu_search"
- android:showAsAction="always|collapseActionView"
- android:icon="@drawable/ic_menu_search_holo_light"
- android:actionLayout="@layout/mail_actionbar_searchview" />
+ <!-- Available only for accounts with SERVER_SEARCH and in a folder
+ that suppors FOLDER_SERVER_SEARCH -->
+ <item android:id="@+id/search" android:title="@string/menu_search"
+ android:showAsAction="always|collapseActionView"
+ android:icon="@drawable/ic_menu_search_holo_light"
+ android:actionLayout="@layout/mail_actionbar_searchview" />
+ <!-- Always available -->
<item android:id="@+id/show_all_folders"
android:title="@string/show_all_folders"
android:showAsAction="ifRoom"
android:icon="@drawable/ic_menu_labels_holo_light" />
+ <!-- Always available -->
<item android:id="@+id/refresh"
android:title="@string/refresh"
android:showAsAction="ifRoom"
android:icon="@drawable/ic_menu_refresh_holo_light"
android:alphabeticShortcut="@string/trigger_refresh_char" />
+ <!-- Always available -->
<item android:id="@+id/label_options"
android:title="@string/menu_label_options"
android:showAsAction="never" />
+ <!-- Always available -->
<item android:id="@+id/preferences"
android:title="@string/menu_preferences"
android:showAsAction="never"
android:icon="@android:drawable/ic_menu_preferences" />
+ <!-- Always available -->
<item
android:id="@+id/help_info_menu_item"
android:icon="@android:drawable/ic_menu_help"
android:showAsAction="never"
android:title="@string/help_and_info" />
+ <!-- Always available -->
<item
android:id="@+id/feedback_menu_item"
android:icon="@android:drawable/ic_menu_send"
diff --git a/src/com/android/mail/AccountSpinnerAdapter.java b/src/com/android/mail/AccountSpinnerAdapter.java
index 800f293..18dde51 100644
--- a/src/com/android/mail/AccountSpinnerAdapter.java
+++ b/src/com/android/mail/AccountSpinnerAdapter.java
@@ -21,7 +21,6 @@
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;
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 9413a01..bb210c5 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -30,7 +30,6 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.util.Log;
import android.view.ActionMode;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -101,13 +100,28 @@
}
@Override
- public void attachConversationList(ConversationListFragment conversationList) {
- mConversationListFragment = conversationList;
+ public synchronized void attachConversationList(ConversationListFragment fragment) {
+ // If there is an existing fragment, unregister it
+ if (mConversationListFragment != null) {
+ mViewMode.removeListener(mConversationListFragment);
+ }
+ mConversationListFragment = fragment;
+ // If the current fragment is non-null, add it as a listener.
+ if (fragment != null) {
+ mViewMode.addListener(mConversationListFragment);
+ }
}
@Override
- public void attachFolderList(FolderListFragment folderList) {
- mFolderListFragment = folderList;
+ public synchronized void attachFolderList(FolderListFragment fragment) {
+ // If there is an existing fragment, unregister it
+ if (mFolderListFragment != null) {
+ mViewMode.removeListener(mFolderListFragment);
+ }
+ mFolderListFragment = fragment;
+ if (fragment != null) {
+ mViewMode.addListener(mFolderListFragment);
+ }
}
@Override
@@ -278,7 +292,14 @@
// Allow shortcut keys to function for the ActionBar and menus.
mActivity.setDefaultKeyMode(Activity.DEFAULT_KEYS_SHORTCUT);
mResolver = mActivity.getContentResolver();
+
+ // All the individual UI components listen for ViewMode changes. This simplifies the
+ // amount of logic in the AbstractActivityController, but increases the possibility of
+ // timing-related bugs.
mViewMode.addListener(this);
+ assert (mActionBarView != null);
+ mViewMode.addListener(mActionBarView);
+
restoreState(savedState);
return true;
}
@@ -407,8 +428,6 @@
*/
@Override
public void onViewModeChanged(int newMode) {
- // Update action bar mode.
- mActionBarView.setMode(newMode);
// Perform any mode specific work here.
// reset the action bar icon based on the mode. Why don't the individual controllers do
// this themselves?
diff --git a/src/com/android/mail/ui/ActionBarView.java b/src/com/android/mail/ui/ActionBarView.java
index 73dbbbd..b6ccdc3 100644
--- a/src/com/android/mail/ui/ActionBarView.java
+++ b/src/com/android/mail/ui/ActionBarView.java
@@ -18,6 +18,7 @@
package com.android.mail.ui;
import android.app.ActionBar;
+import android.app.ActionBar.OnNavigationListener;
import android.os.Bundle;
import android.view.Menu;
@@ -26,7 +27,7 @@
* pre-v14 devices).
*/
-public interface ActionBarView {
+public interface ActionBarView extends OnNavigationListener, ViewMode.ModeChangeListener {
/**
* Initialize the ActionBarView
* @param activity
@@ -38,25 +39,12 @@
ViewMode viewMode, ActionBar actionBar);
/**
- * Return the mode that the action bar is in.
- * @return The mode the action bar is in.
- */
- int getMode();
-
- /**
* Handle handleRestore from the Android framework.
* @param savedInstanceState
*/
void handleRestore(Bundle savedInstanceState);
/**
- * Change the mode of the actionbar.
- * @param mode
- * @return true if the change in mode was successful.
- */
- boolean setMode(int mode);
-
- /**
* Handle onResume from the Android framework.
*/
void onResume();
@@ -113,7 +101,7 @@
boolean createOptionsMenu(Menu menu);
/**
- * Update sthe action bar based on a new status received from the server.
+ * Updates the action bar based on a new status received from the server.
* @param account
* @param status
*/
diff --git a/src/com/android/mail/ui/FolderListFragment.java b/src/com/android/mail/ui/FolderListFragment.java
index 95d168e..1fe42d5 100644
--- a/src/com/android/mail/ui/FolderListFragment.java
+++ b/src/com/android/mail/ui/FolderListFragment.java
@@ -43,7 +43,7 @@
* The folder list UI component.
*/
public final class FolderListFragment extends ListFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+ LoaderManager.LoaderCallbacks<Cursor>, ViewMode.ModeChangeListener {
private static final String LOG_TAG = new LogUtils().getLogTag();
private ControllableActivity mActivity;
@@ -209,4 +209,9 @@
return folderItemView;
}
}
+
+ @Override
+ public void onViewModeChanged(int newMode) {
+ // Listen on mode changes, when we move to Label list mode, change accordingly.
+ }
}
diff --git a/src/com/android/mail/ui/MailActionBar.java b/src/com/android/mail/ui/MailActionBar.java
index fe91058..00a7575 100644
--- a/src/com/android/mail/ui/MailActionBar.java
+++ b/src/com/android/mail/ui/MailActionBar.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -35,19 +36,24 @@
import com.android.mail.AccountSpinnerAdapter;
import com.android.mail.ConversationListContext;
import com.android.mail.providers.Account;
+import com.android.mail.providers.UIProvider.AccountCapabilities;
/**
- * View to manage the various states of the Gmail Action Bar
+ * View to manage the various states of the Mail Action Bar
*
- * TODO(viki): Include ConversatinSubjectDisplayer here as well.
+ * TODO(viki): Include ConversationSubjectDisplayer here as well.
*/
-public class MailActionBar extends LinearLayout implements ActionBarView, OnNavigationListener {
+public final class MailActionBar extends LinearLayout implements ActionBarView {
/**
* This interface is used to send notifications back to the calling
* activity. MenuHandler takes care of updating the provider, so this
* interface should be used for notification purposes only (such as updating
* the UI).
*/
+ // TODO(viki): This callback is currently unused and may be entirely unnecessary in the new
+ // code, where the Actionbar is switched into navigation mode, relying on the framework for most
+ // heavy lifting. Also, we can switch ViewMode to the appropriate mode and rely on all UI
+ // components updating through ViewMode change listeners.
public interface Callback {
/**
* Enter search mode
@@ -94,16 +100,23 @@
private String[] mAccountNames;
private ActionBar mActionBar;
- protected RestrictedActivity mActivity;
+ private RestrictedActivity mActivity;
private Callback mCallback;
- protected View mFolderView;
- private int mMode;
+ private View mFolderView;
+ /**
+ * The current mode of the ActionBar. This references constants in {@link ViewMode}
+ */
+ private int mMode = ViewMode.UNKNOWN;
private MenuItem mRefreshItem;
private MenuItem mSearch;
SpinnerAdapter mSpinner;
- protected AccountRecentLabelSpinner mSpinnerView;
+ private AccountRecentLabelSpinner mSpinnerView;
+ /**
+ * We need to know what each account is capable of, so we can tailor the menu accordingly.
+ */
+ private int mAccountCapabilities;
// TODO(viki): This is a SnippetTextView in the Gmail source code. Resolve.
private TextView mSubjectView;
@@ -116,10 +129,8 @@
this(context, attrs, 0);
}
-
public MailActionBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mMode = ViewMode.UNKNOWN;
}
@Override
@@ -135,25 +146,21 @@
}
@Override
- public int getMode() {
- return mMode;
- }
-
- @Override
public int getOptionsMenuId() {
- switch (mMode){
- case ViewMode.UNKNOWN:
- // Fallthrough
- case ViewMode.SEARCH_RESULTS:
- return R.menu.conversation_list_menu;
- case ViewMode.FOLDER_LIST:
- return R.menu.folder_list_menu;
- case ViewMode.CONVERSATION_LIST:
- return R.menu.conversation_list_menu;
- case ViewMode.CONVERSATION:
- return R.menu.conversation_actions;
- }
- return 0;
+ // Relies on the ordering of the view modes, since they are integer constants.
+ final int[] modeMenu = {
+ // 0: UNKNOWN
+ R.menu.conversation_list_menu,
+ // 1: CONVERSATION
+ R.menu.conversation_actions,
+ // 2: CONVERSATION_LIST
+ R.menu.conversation_list_menu,
+ // 3: FOLDER_LIST
+ R.menu.folder_list_menu,
+ // 4: SEARCH_RESULTS
+ R.menu.conversation_list_menu
+ };
+ return modeMenu[mMode];
}
@Override
@@ -184,12 +191,16 @@
}
@Override
- public boolean onNavigationItemSelected(int itemPosition, long itemId) {
- // Don't do anything. Toast on the action.
- int type = mSpinner.getItemViewType(itemPosition);
+ public boolean onNavigationItemSelected(int position, long id) {
+ final int type = mSpinner.getItemViewType(position);
switch (type) {
case AccountSpinnerAdapter.TYPE_ACCOUNT:
- mCallback.navigateToAccount((Account) mSpinner.getItem(itemPosition));
+ mCallback.navigateToAccount((Account) mSpinner.getItem(position));
+ // Get the capabilities associated with this account.
+ final Object item = mSpinner.getItem(position);
+ assert (item instanceof Account);
+ mAccountCapabilities = ((Account) item).capabilities;
+ Log.d("viki", "Account capabilities are " + mAccountCapabilities);
break;
}
return false;
@@ -214,7 +225,30 @@
}
@Override
+ public void onViewModeChanged(int newMode) {
+ mMode = newMode;
+
+ // Always update the options menu and redraw. This will read the new mode and redraw
+ // the options menu.
+ mActivity.invalidateOptionsMenu();
+ }
+
+ /**
+ * If shouldSetView is true, then the view is made visible, otherwise its visiblity is View.GONE
+ * @param view the view whose visibility is modified
+ * @param shouldSetView if true, the view is made visible, GONE otherwise
+ */
+ private void setVisibility(int resourceId, boolean shouldSetView) {
+ final View view = findViewById(resourceId);
+ assert (view != null);
+ final int visibility = shouldSetView ? View.VISIBLE : View.GONE;
+ view.setVisibility(visibility);
+ }
+
+ @Override
public boolean prepareOptionsMenu(Menu menu) {
+ // We start out with every option enabled. Based on the current view, we disable actions
+ // that are possible.
if (mSubjectView != null){
mSubjectView.setVisibility(GONE);
}
@@ -230,7 +264,10 @@
}
break;
case ViewMode.CONVERSATION_LIST:
- // Do nothing?
+ // Show compose, search, labels, and sync based on the account
+ // The only option that needs to be disabled is search
+ setVisibility (R.id.search,
+ (mAccountCapabilities & AccountCapabilities.FOLDER_SERVER_SEARCH) != 0);
break;
case ViewMode.CONVERSATION:
// Do nothing?
@@ -275,12 +312,6 @@
}
@Override
- public boolean setMode(int mode) {
- mMode = mode;
- return true;
- }
-
- @Override
public void updateActionBar(String[] accounts, String currentAccount) {
}
}
diff --git a/src/com/android/mail/ui/MailActivity.java b/src/com/android/mail/ui/MailActivity.java
index 7bc587f..01197ca 100644
--- a/src/com/android/mail/ui/MailActivity.java
+++ b/src/com/android/mail/ui/MailActivity.java
@@ -54,6 +54,10 @@
private ViewMode mViewMode;
+ public MailActivity() {
+ super();
+ }
+
@Override
public void attachConversationList(ConversationListFragment fragment) {
mController.attachConversationList(fragment);
diff --git a/src/com/android/mail/ui/OnePaneController.java b/src/com/android/mail/ui/OnePaneController.java
index eeb18a4..12239f9 100644
--- a/src/com/android/mail/ui/OnePaneController.java
+++ b/src/com/android/mail/ui/OnePaneController.java
@@ -32,14 +32,13 @@
// Called OnePaneActivityController in Gmail.
public final class OnePaneController extends AbstractActivityController {
-
+ private boolean mConversationListVisible = false;
/**
* @param activity
* @param viewMode
*/
public OnePaneController(MailActivity activity, ViewMode viewMode) {
super(activity, viewMode);
- // TODO(viki): Auto-generated constructor stub
}
@Override
@@ -63,65 +62,54 @@
@Override
protected boolean isConversationListVisible() {
- // TODO(viki): Auto-generated method stub
- return false;
+ return mConversationListVisible;
}
@Override
public void onViewModeChanged(int newMode) {
super.onViewModeChanged(newMode);
-
- // We don't want to invalidate the options menu when switching to conversation
- // mode, as it will happen when the conversation finishes loading.
- if (newMode != ViewMode.CONVERSATION) {
- mActivity.invalidateOptionsMenu();
- }
}
@Override
public void showConversationList(ConversationListContext listContext) {
mViewMode.enterConversationListMode();
- FragmentTransaction fragmentTransaction = mActivity.getFragmentManager().beginTransaction();
- fragmentTransaction.addToBackStack(null);
+ // TODO(viki): Check if the account has been changed since the previous time.
final boolean accountChanged = false;
// TODO(viki): This account transition looks strange in two pane mode.
- // Revisit as the app
- // is coming together and improve the look and feel.
+ // Revisit as the app is coming together and improve the look and feel.
final int transition = accountChanged ? FragmentTransaction.TRANSIT_FRAGMENT_FADE
: FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
if (listContext == null) {
listContext = getCurrentListContext();
}
- fragmentTransaction.setTransition(transition);
Fragment conversationListFragment = ConversationListFragment.newInstance(listContext);
- fragmentTransaction.replace(R.id.content_pane, conversationListFragment);
-
- fragmentTransaction.commitAllowingStateLoss();
- resetActionBarIcon();
+ replaceFragment(conversationListFragment, transition);
+ mConversationListVisible = true;
}
@Override
public void showConversation(Conversation conversation) {
mViewMode.enterConversationMode();
- replaceFragment(ConversationViewFragment.newInstance(mAccount, conversation));
+ replaceFragment(ConversationViewFragment.newInstance(mAccount, conversation),
+ FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+ mConversationListVisible = false;
}
@Override
public void showFolderList() {
mViewMode.enterFolderListMode();
- replaceFragment(FolderListFragment.newInstance(this, mAccount.folderListUri));
+ replaceFragment(FolderListFragment.newInstance(this, mAccount.folderListUri),
+ FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+ mConversationListVisible = false;
}
- private void replaceFragment(Fragment fragment) {
+ private void replaceFragment(Fragment fragment, int transition) {
FragmentTransaction fragmentTransaction = mActivity.getFragmentManager().beginTransaction();
fragmentTransaction.addToBackStack(null);
- final int transition = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
fragmentTransaction.setTransition(transition);
-
fragmentTransaction.replace(R.id.content_pane, fragment);
-
fragmentTransaction.commitAllowingStateLoss();
resetActionBarIcon();
}
@@ -153,7 +141,6 @@
private void transitionBackToConversationListMode() {
mViewMode.enterConversationListMode();
- mActivity.invalidateOptionsMenu();
resetActionBarIcon();
}
}
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 0b22e02..9fdd3d7 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -26,7 +26,6 @@
import android.os.Bundle;
import android.view.Window;
-
/**
* Controller for one-pane Mail activity. One Pane is used for phones, where screen real estate is
* limited.
diff --git a/src/com/android/mail/ui/ViewMode.java b/src/com/android/mail/ui/ViewMode.java
index 6357b47..40aad55 100644
--- a/src/com/android/mail/ui/ViewMode.java
+++ b/src/com/android/mail/ui/ViewMode.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.os.Bundle;
-
import java.util.ArrayList;
/**
@@ -92,6 +91,7 @@
mMode = newMode;
ArrayList<ModeChangeListener> list = new ArrayList<ModeChangeListener>(mListeners);
for (ModeChangeListener listener : list) {
+ assert (listener != null);
listener.onViewModeChanged(newMode);
}
}