Fix Fragment BackStack and Titles

Related to bug #12939786 and bug #13223838

- first use Fragment BreadCrumb for managing Titles and Back
- consider the first fragment as the initial one (both in the
normal case and in the shortcut one)
- fix usage of the Fragment BackStack so that in all cases we
are returning to the initial Fragment before exiting the App.

Change-Id: I96989d14f4e88688747b93ab9fadd96aea214a2c
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 4430138..b7d29fa 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -62,10 +62,8 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Pair;
 import android.util.TypedValue;
 import android.util.Xml;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
@@ -200,6 +198,8 @@
     private Header mSelectedHeader;
     private Header mCurrentHeader;
 
+    private CharSequence mInitialTitle;
+
     // Show only these settings for restricted users
     private int[] SETTINGS_FOR_RESTRICTED = {
             R.id.wireless_section,
@@ -313,39 +313,6 @@
     private final ArrayList<Header> mHeaders = new ArrayList<Header>();
     private HeaderAdapter mHeaderAdapter;
 
-    static private class TitlePair extends Pair<Integer, CharSequence> implements Parcelable {
-
-        public TitlePair(Integer first, CharSequence second) {
-            super(first, second);
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(first);
-            TextUtils.writeToParcel(second, dest, flags);
-        }
-
-        TitlePair(Parcel in) {
-            super(in.readInt(), TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
-        }
-
-        public static final Creator<TitlePair> CREATOR = new Creator<TitlePair>() {
-            public TitlePair createFromParcel(Parcel source) {
-                return new TitlePair(source);
-            }
-            public TitlePair[] newArray(int size) {
-                return new TitlePair[size];
-            }
-        };
-    }
-
-    private final ArrayList<TitlePair> mTitleStack = new ArrayList<TitlePair>();
-
     private DrawerLayout mDrawerLayout;
     private ListView mDrawer;
     private ActionBarDrawerToggle mDrawerToggle;
@@ -408,7 +375,7 @@
             if (isFinishing() || mSelectedHeader == null) {
                 return;
             }
-            switchToHeader(mSelectedHeader, false);
+            switchToHeader(mSelectedHeader, false, false);
             mSelectedHeader = null;
         }
 
@@ -556,17 +523,8 @@
         if (savedInstanceState != null) {
             // We are restarting from a previous saved state; used that to
             // initialize, instead of starting fresh.
-
-            ArrayList<TitlePair> titles =
-                    savedInstanceState.getParcelableArrayList(SAVE_KEY_TITLES_TAG);
-            if (titles != null) {
-                mTitleStack.addAll(titles);
-            }
-            final int lastTitle = mTitleStack.size() - 1;
-            if (lastTitle >= 0) {
-                final TitlePair last = mTitleStack.get(lastTitle);
-                setTitleFromPair(last);
-            }
+            mInitialTitle = getTitle();
+            setTitleFromBackStack();
 
             ArrayList<Header> headers =
                     savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG);
@@ -578,18 +536,15 @@
                     setSelectedHeader(mHeaders.get(curHeader));
                 }
             }
-
         } else {
             if (initialFragment != null) {
                 // If we are just showing a fragment, we want to run in
                 // new fragment mode, but don't need to compute and show
                 // the headers.
-                switchToHeader(initialFragment, initialArguments, true);
+                final int initialTitleResId = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
+                mInitialTitle = (initialTitleResId > 0) ? getText(initialTitleResId) : getTitle();
 
-                final int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
-                if (initialTitle != 0) {
-                    setTitle(getText(initialTitle));
-                }
+                switchToHeader(initialFragment, initialArguments, true, mInitialTitle);
             } else {
                 // We need to try to build the headers.
                 onBuildHeaders(mHeaders);
@@ -599,7 +554,8 @@
                 // the currently selected preference fragment.
                 if (mHeaders.size() > 0) {
                     Header h = onGetInitialHeader();
-                    switchToHeader(h, false);
+                    mInitialTitle = getHeaderTitle(h);
+                    switchToHeader(h, false, true);
                 }
             }
         }
@@ -666,32 +622,30 @@
 
     @Override
     public void onBackStackChanged() {
-        final int count = getFragmentManager().getBackStackEntryCount() + 1;
-        TitlePair pair = null;
-        int last;
-        while (mTitleStack.size() > count) {
-            last = mTitleStack.size() - 1;
-            pair = mTitleStack.remove(last);
-        }
-        // Check if we go back
-        if (pair != null) {
-            int size = mTitleStack.size();
-            if (size > 0) {
-                last = mTitleStack.size() - 1;
-                pair = mTitleStack.get(last);
-                setTitleFromPair(pair);
-            }
-        }
+        setTitleFromBackStack();
     }
 
-    private void setTitleFromPair(TitlePair pair) {
-        final CharSequence title;
-        if (pair.first > 0) {
-            title = getText(pair.first);
-        } else {
-            title = pair.second;
+    private void setTitleFromBackStack() {
+        final int count = getFragmentManager().getBackStackEntryCount();
+        if (count == 0) {
+            setTitle(mInitialTitle);
+            return;
         }
-        setTitle(title);
+        FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
+        setTitleFromBackStackEntry(bse);
+    }
+
+    private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
+        final CharSequence title;
+        final int titleRes = bse.getBreadCrumbTitleRes();
+        if (titleRes > 0) {
+            title = getText(titleRes);
+        } else {
+            title = bse.getBreadCrumbTitle();
+        }
+        if (title != null) {
+            setTitle(title);
+        }
     }
 
     /**
@@ -714,10 +668,6 @@
                 }
             }
         }
-
-        if (mTitleStack.size() > 0) {
-            outState.putParcelableList(SAVE_KEY_TITLES_TAG, mTitleStack);
-        }
     }
 
     @Override
@@ -782,9 +732,10 @@
      * preference fragment.
      *
      * @param header The new header to display.
-     * @param validate true means that the fragment's Header needs to be validated
+     * @param validate true means that the fragment's Header needs to be validated.
+     * @param initial true means that it is the initial Header.
      */
-    private void switchToHeader(Header header, boolean validate) {
+    private void switchToHeader(Header header, boolean validate, boolean initial) {
         if (header == null) {
             return;
         }
@@ -798,15 +749,11 @@
                     FragmentManager.POP_BACK_STACK_INCLUSIVE);
         } else {
             if (header.fragment != null) {
-                mTitleStack.clear();
-                switchToHeaderInner(header.fragment, header.fragmentArguments, validate);
+                switchToHeaderInner(header.fragment, header.fragmentArguments, validate, !initial,
+                        getHeaderTitle(header));
                 setSelectedHeader(header);
-                final TitlePair pair = new TitlePair(0, getHeaderTitle(header));
-                mTitleStack.add(pair);
-                setTitle(pair.second);
             } else if (header.intent != null) {
                 setSelectedHeader(header);
-                mTitleStack.clear();
                 startActivity(header.intent);
             } else {
                 throw new IllegalStateException(
@@ -860,19 +807,20 @@
     }
 
     /**
-     * When in two-pane mode, switch the fragment pane to show the given
-     * preference fragment.
+     * Switch the fragment pane to show the given preference fragment.
      *
      * @param fragmentName The name of the fragment to display.
      * @param args Optional arguments to supply to the fragment.
      * @param validate true means that the fragment's Header needs to be validated
      */
-    private void switchToHeader(String fragmentName, Bundle args, boolean validate) {
+    private void switchToHeader(String fragmentName, Bundle args, boolean validate,
+                                CharSequence title) {
         setSelectedHeader(null);
-        switchToHeaderInner(fragmentName, args, validate);
+        switchToHeaderInner(fragmentName, args, validate, false, title);
     }
 
-    private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate) {
+    private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
+                                     boolean addToBackStack, CharSequence title) {
         getFragmentManager().popBackStack(BACK_STACK_PREFS,
                 FragmentManager.POP_BACK_STACK_INCLUSIVE);
         if (validate && !isValidFragment(fragmentName)) {
@@ -881,8 +829,14 @@
         }
         Fragment f = Fragment.instantiate(this, fragmentName, args);
         FragmentTransaction transaction = getFragmentManager().beginTransaction();
-        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
         transaction.replace(R.id.prefs, f);
+        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
+        if (addToBackStack) {
+            transaction.addToBackStack(BACK_STACK_PREFS);
+        }
+        if (title != null) {
+            transaction.setBreadCrumbTitle(title);
+        }
         transaction.commitAllowingStateLoss();
     }
 
@@ -1036,6 +990,13 @@
      */
     private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
                                    int resultRequestCode, int titleRes, CharSequence titleText) {
+        final CharSequence cs;
+        if (titleRes != 0) {
+            cs = getText(titleRes);
+        } else {
+            cs = titleText;
+        }
+
         Fragment f = Fragment.instantiate(this, fragmentName, args);
         if (resultTo != null) {
             f.setTargetFragment(resultTo, resultRequestCode);
@@ -1044,19 +1005,8 @@
         transaction.replace(R.id.prefs, f);
         transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
         transaction.addToBackStack(BACK_STACK_PREFS);
+        transaction.setBreadCrumbTitle(cs);
         transaction.commitAllowingStateLoss();
-
-        final TitlePair pair;
-        final CharSequence cs;
-        if (titleRes != 0) {
-            pair = new TitlePair(titleRes, null);
-            cs = getText(titleRes);
-        } else {
-            pair = new TitlePair(0, titleText);
-            cs = titleText;
-        }
-        setTitle(cs);
-        mTitleStack.add(pair);
     }
 
     /**