Fix bug 3156280 - Fix several issues with tab navigation in action bars.
Add the ability to restrict a FragmentTransaction's ability to be
added to the back stack. (It doesn't make sense for tabs or other
scenarios to allow this.)
Change-Id: I8fa2edb5f35c365e2483010ad13eb9993f5e6570
diff --git a/api/current.xml b/api/current.xml
index f14df1d..0cf46eb 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19525,6 +19525,17 @@
<parameter name="index" type="int">
</parameter>
</method>
+<method name="getTabCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getTitle"
return="java.lang.CharSequence"
abstract="true"
@@ -28322,6 +28333,17 @@
visibility="public"
>
</method>
+<method name="disallowAddToBackStack"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="hide"
return="android.app.FragmentTransaction"
abstract="true"
@@ -28335,6 +28357,17 @@
<parameter name="fragment" type="android.app.Fragment">
</parameter>
</method>
+<method name="isAddToBackStackAllowed"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isEmpty"
return="boolean"
abstract="true"
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index e185624..a57b54a 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -478,6 +478,12 @@
public abstract Tab getTabAt(int index);
/**
+ * Returns the number of tabs currently registered with the action bar.
+ * @return Tab count
+ */
+ public abstract int getTabCount();
+
+ /**
* Retrieve the current height of the ActionBar.
*
* @return The ActionBar's height
@@ -626,7 +632,8 @@
* @param tab The tab that was selected
* @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
* during a tab switch. The previous tab's unselect and this tab's select will be
- * executed in a single transaction.
+ * executed in a single transaction. This FragmentTransaction does not support
+ * being added to the back stack.
*/
public void onTabSelected(Tab tab, FragmentTransaction ft);
@@ -636,7 +643,8 @@
* @param tab The tab that was unselected
* @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
* during a tab switch. This tab's unselect and the newly selected tab's select
- * will be executed in a single transaction.
+ * will be executed in a single transaction. This FragmentTransaction does not
+ * support being added to the back stack.
*/
public void onTabUnselected(Tab tab, FragmentTransaction ft);
@@ -646,7 +654,8 @@
*
* @param tab The tab that was reselected.
* @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
- * once this method returns.
+ * once this method returns. This FragmentTransaction does not support
+ * being added to the back stack.
*/
public void onTabReselected(Tab tab, FragmentTransaction ft);
}
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index e6cc0f9..c75777d 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -186,6 +186,7 @@
int mTransition;
int mTransitionStyle;
boolean mAddToBackStack;
+ boolean mAllowAddToBackStack = true;
String mName;
boolean mCommitted;
int mIndex;
@@ -346,11 +347,28 @@
}
public FragmentTransaction addToBackStack(String name) {
+ if (!mAllowAddToBackStack) {
+ throw new IllegalStateException(
+ "This FragmentTransaction is not allowed to be added to the back stack.");
+ }
mAddToBackStack = true;
mName = name;
return this;
}
+ public boolean isAddToBackStackAllowed() {
+ return mAllowAddToBackStack;
+ }
+
+ public FragmentTransaction disallowAddToBackStack() {
+ if (mAddToBackStack) {
+ throw new IllegalStateException(
+ "This transaction is already being added to the back stack");
+ }
+ mAllowAddToBackStack = false;
+ return this;
+ }
+
public FragmentTransaction setBreadCrumbTitle(int res) {
mBreadCrumbTitleRes = res;
mBreadCrumbTitleText = null;
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index b00476bb..19da763 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -144,6 +144,22 @@
public FragmentTransaction addToBackStack(String name);
/**
+ * Returns true if this FragmentTransaction is allowed to be added to the back
+ * stack. If this method would return false, {@link #addToBackStack(String)}
+ * will throw {@link IllegalStateException}.
+ *
+ * @return True if {@link #addToBackStack(String)} is permitted on this transaction.
+ */
+ public boolean isAddToBackStackAllowed();
+
+ /**
+ * Disallow calls to {@link #addToBackStack(String)}. Any future calls to
+ * addToBackStack will throw {@link IllegalStateException}. If addToBackStack
+ * has already been called, this method will throw IllegalStateException.
+ */
+ public FragmentTransaction disallowAddToBackStack();
+
+ /**
* Set the full title to show as a bread crumb when this transaction
* is on the back stack, as used by {@link FragmentBreadCrumbs}.
*
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 1d612e2..7cf369f 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -65,12 +65,15 @@
private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
private TabImpl mSelectedTab;
+ private int mSavedTabPosition = INVALID_POSITION;
private ActionMode mActionMode;
private static final int CONTEXT_DISPLAY_NORMAL = 0;
private static final int CONTEXT_DISPLAY_SPLIT = 1;
+ private static final int INVALID_POSITION = -1;
+
private int mContextDisplayMode;
private boolean mClosingContext;
@@ -183,6 +186,8 @@
selectTab(null);
}
mTabs.clear();
+ mActionView.removeAllTabs();
+ mSavedTabPosition = INVALID_POSITION;
}
public void setTitle(CharSequence title) {
@@ -310,6 +315,8 @@
@Override
public void removeTabAt(int position) {
+ int selectedTabPosition = mSelectedTab != null
+ ? mSelectedTab.getPosition() : mSavedTabPosition;
mActionView.removeTabAt(position);
mTabs.remove(position);
@@ -318,7 +325,9 @@
mTabs.get(i).setPosition(i);
}
- selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
+ if (selectedTabPosition == position) {
+ selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
+ }
}
@Override
@@ -333,7 +342,13 @@
@Override
public void selectTab(Tab tab) {
- final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction();
+ if (getNavigationMode() != NAVIGATION_MODE_TABS) {
+ mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
+ return;
+ }
+
+ final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction()
+ .disallowAddToBackStack();
if (mSelectedTab == tab) {
if (mSelectedTab != null) {
@@ -623,12 +638,52 @@
}
@Override
+ public int getTabCount() {
+ return mTabs.size();
+ }
+
+ @Override
public void setNavigationMode(int mode) {
+ final int oldMode = mActionView.getNavigationMode();
+ switch (oldMode) {
+ case NAVIGATION_MODE_TABS:
+ mSavedTabPosition = getSelectedNavigationIndex();
+ selectTab(null);
+ break;
+ }
mActionView.setNavigationMode(mode);
+ switch (mode) {
+ case NAVIGATION_MODE_TABS:
+ if (mSavedTabPosition != INVALID_POSITION) {
+ setSelectedNavigationItem(mSavedTabPosition);
+ mSavedTabPosition = INVALID_POSITION;
+ }
+ break;
+ }
}
@Override
public Tab getTabAt(int index) {
return mTabs.get(index);
}
+
+ /**
+ * This fragment is added when we're keeping a back stack in a tab switch
+ * transaction. We use it to change the selected tab in the action bar view
+ * when we back out.
+ */
+ private class SwitchSelectedTabViewFragment extends Fragment {
+ private int mSelectedTabIndex;
+
+ public SwitchSelectedTabViewFragment(int oldSelectedTab) {
+ mSelectedTabIndex = oldSelectedTab;
+ }
+
+ @Override
+ public void onDetach() {
+ if (mSelectedTabIndex >= 0 && mSelectedTabIndex < getTabCount()) {
+ mActionView.setTabSelected(mSelectedTabIndex);
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 95d6dd3..8ead9c8 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -487,6 +487,12 @@
}
}
+ public void removeAllTabs() {
+ if (mTabLayout != null) {
+ mTabLayout.removeAllViews();
+ }
+ }
+
@Override
protected LayoutParams generateDefaultLayoutParams() {
// Used by custom nav views if they don't supply layout params. Everything else