Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 61fc0d47 authored by Filip Pavlis's avatar Filip Pavlis
Browse files

Fix transitions between single and multi-pane.

Transitions in and out of multi-window were causing navigation history
losses + incorrect views to be displayed. The history loss was caused by
the fact that single-pane mode was using Activities but multi-pane mode
was using Fragments. Translating history between those two would be
challenging. Because of that Fragment approach was also applied to
single-pane mode. That required some changes in back press handling and
initial logic when creating the PreferenceActivity.

Test: Separate CTS CL being submitted along this one, see the topic.
Bug: 32201932
Change-Id: I5e6a6a21c34e813d1c3522ae7f5e33f43887c602
parent d61cc80a
Loading
Loading
Loading
Loading
+115 −107
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.preference;

import android.animation.LayoutTransition;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.XmlRes;
@@ -199,6 +200,9 @@ public abstract class PreferenceActivity extends ListActivity implements

    private ViewGroup mPrefsContainer;

    // Null if in legacy mode.
    private ViewGroup mHeadersContainer;

    private FragmentBreadCrumbs mFragmentBreadCrumbs;

    private boolean mSinglePane;
@@ -558,6 +562,7 @@ public abstract class PreferenceActivity extends ListActivity implements

        mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer);
        mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs_frame);
        mHeadersContainer = (ViewGroup) findViewById(com.android.internal.R.id.headers);
        boolean hidingHeaders = onIsHidingHeaders();
        mSinglePane = hidingHeaders || !onIsMultiPane();
        String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
@@ -575,66 +580,39 @@ public abstract class PreferenceActivity extends ListActivity implements
                        (int) HEADER_ID_UNDEFINED);
                if (curHeader >= 0 && curHeader < mHeaders.size()) {
                    setSelectedHeader(mHeaders.get(curHeader));
                } else if (!mSinglePane && initialFragment == null) {
                    switchToHeader(onGetInitialHeader());
                }
            }

            } else {
            if (initialFragment != null && mSinglePane) {
                // 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);
                if (initialTitle != 0) {
                    CharSequence initialTitleStr = getText(initialTitle);
                    CharSequence initialShortTitleStr = initialShortTitle != 0
                            ? getText(initialShortTitle) : null;
                    showBreadCrumbs(initialTitleStr, initialShortTitleStr);
                // This will for instance hide breadcrumbs for single pane.
                showBreadCrumbs(getTitle(), null);
            }

        } else {
                // We need to try to build the headers.
            if (!onIsHidingHeaders()) {
                onBuildHeaders(mHeaders);
            }

                // If there are headers, then at this point we need to show
                // them and, depending on the screen, we may also show in-line
                // the currently selected preference fragment.
                if (mHeaders.size() > 0) {
                    if (!mSinglePane) {
                        if (initialFragment == null) {
                            Header h = onGetInitialHeader();
                            switchToHeader(h);
                        } else {
            if (initialFragment != null) {
                switchToHeader(initialFragment, initialArguments);
            } else if (!mSinglePane && mHeaders.size() > 0) {
                switchToHeader(onGetInitialHeader());
            }
        }
                }
            }

        if (mHeaders.size() > 0) {
            setListAdapter(new HeaderAdapter(this, mHeaders, mPreferenceHeaderItemResId,
                    mPreferenceHeaderRemoveEmptyIcon));
            getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
        }

        // The default configuration is to only show the list view.  Adjust
        // visibility for other configurations.
        if (initialFragment != null && mSinglePane) {
            // Single pane, showing just a prefs fragment.
            findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
            mPrefsContainer.setVisibility(View.VISIBLE);
            if (initialTitle != 0) {
        if (mSinglePane && initialFragment != null && initialTitle != 0) {
            CharSequence initialTitleStr = getText(initialTitle);
            CharSequence initialShortTitleStr = initialShortTitle != 0
                    ? getText(initialShortTitle) : null;
            showBreadCrumbs(initialTitleStr, initialShortTitleStr);
        }
        } else if (mHeaders.size() > 0) {
            setListAdapter(new HeaderAdapter(this, mHeaders, mPreferenceHeaderItemResId,
                    mPreferenceHeaderRemoveEmptyIcon));
            if (!mSinglePane) {
                // Multi-pane.
                getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
                if (mCurHeader != null) {
                    setSelectedHeader(mCurHeader);
                }
                mPrefsContainer.setVisibility(View.VISIBLE);
            }
        } else {

        if (mHeaders.size() == 0 && initialFragment == null) {
            // If there are no headers, we are in the old "just show a screen
            // of preferences" mode.
            setContentView(com.android.internal.R.layout.preference_list_content_single);
@@ -642,6 +620,25 @@ public abstract class PreferenceActivity extends ListActivity implements
            mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs);
            mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
            mPreferenceManager.setOnPreferenceTreeClickListener(this);
            mHeadersContainer = null;
        } else if (mSinglePane) {
            // Single-pane so one of the header or prefs containers must be hidden.
            if (initialFragment != null || mCurHeader != null) {
                mHeadersContainer.setVisibility(View.GONE);
            } else {
                mPrefsContainer.setVisibility(View.GONE);
            }

            // This animates our manual transitions between headers and prefs panel in single-pane.
            // It also comes last so we don't animate any initial layout changes done above.
            ViewGroup container = (ViewGroup) findViewById(
                    com.android.internal.R.id.prefs_container);
            container.setLayoutTransition(new LayoutTransition());
        } else {
            // Multi-pane
            if (mHeaders.size() > 0 && mCurHeader != null) {
                setSelectedHeader(mCurHeader);
            }
        }

        // see if we should show Back/Next buttons
@@ -697,12 +694,25 @@ public abstract class PreferenceActivity extends ListActivity implements
        }
    }

    @Override
    public void onBackPressed() {
        if (mCurHeader != null && mSinglePane && getFragmentManager().getBackStackEntryCount() == 0
                && getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null) {
            mCurHeader = null;

            mPrefsContainer.setVisibility(View.GONE);
            mHeadersContainer.setVisibility(View.VISIBLE);
            getListView().clearChoices();
        } else {
            super.onBackPressed();
        }
    }

    /**
     * Returns true if this activity is currently showing the header list.
     */
    public boolean hasHeaders() {
        return getListView().getVisibility() == View.VISIBLE
                && mPreferenceManager == null;
        return mHeadersContainer != null && mHeadersContainer.getVisibility() == View.VISIBLE;
    }

    /**
@@ -718,7 +728,7 @@ public abstract class PreferenceActivity extends ListActivity implements
     * and a preference fragment.
     */
    public boolean isMultiPane() {
        return hasHeaders() && mPrefsContainer.getVisibility() == View.VISIBLE;
        return !mSinglePane;
    }

    /**
@@ -939,7 +949,8 @@ public abstract class PreferenceActivity extends ListActivity implements
        if (getApplicationInfo().targetSdkVersion  >= android.os.Build.VERSION_CODES.KITKAT) {
            throw new RuntimeException(
                    "Subclasses of PreferenceActivity must override isValidFragment(String)"
                    + " to verify that the Fragment class is valid! " + this.getClass().getName()
                            + " to verify that the Fragment class is valid! "
                            + this.getClass().getName()
                            + " has not checked if fragment " + fragmentName + " is valid.");
        } else {
            return true;
@@ -1017,6 +1028,13 @@ public abstract class PreferenceActivity extends ListActivity implements
        // Only call this if we didn't save the instance state for later.
        // If we did save it, it will be restored when we bind the adapter.
        super.onRestoreInstanceState(state);

        if (!mSinglePane) {
            // Multi-pane.
            if (mCurHeader != null) {
                setSelectedHeader(mCurHeader);
            }
        }
    }

    @Override
@@ -1061,18 +1079,7 @@ public abstract class PreferenceActivity extends ListActivity implements
     */
    public void onHeaderClick(Header header, int position) {
        if (header.fragment != null) {
            if (mSinglePane) {
                int titleRes = header.breadCrumbTitleRes;
                int shortTitleRes = header.breadCrumbShortTitleRes;
                if (titleRes == 0) {
                    titleRes = header.titleRes;
                    shortTitleRes = 0;
                }
                startWithFragment(header.fragment, header.fragmentArguments, null, 0,
                        titleRes, shortTitleRes);
            } else {
            switchToHeader(header);
            }
        } else if (header.intent != null) {
            startActivity(header.intent);
        }
@@ -1223,11 +1230,21 @@ public abstract class PreferenceActivity extends ListActivity implements
            throw new IllegalArgumentException("Invalid fragment for this activity: "
                    + fragmentName);
        }

        Fragment f = Fragment.instantiate(this, fragmentName, args);
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        transaction.setTransition(mSinglePane
                ? FragmentTransaction.TRANSIT_NONE
                : FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        transaction.replace(com.android.internal.R.id.prefs, f);
        transaction.commitAllowingStateLoss();

        if (mSinglePane && mPrefsContainer.getVisibility() == View.GONE) {
            // We are transitioning from headers to preferences panel in single-pane so we need
            // to hide headers and show the prefs container.
            mPrefsContainer.setVisibility(View.VISIBLE);
            mHeadersContainer.setVisibility(View.GONE);
        }
    }

    /**
@@ -1355,9 +1372,6 @@ public abstract class PreferenceActivity extends ListActivity implements
     */
    public void startPreferencePanel(String fragmentClass, Bundle args, @StringRes int titleRes,
            CharSequence titleText, Fragment resultTo, int resultRequestCode) {
        if (mSinglePane) {
            startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, 0);
        } else {
        Fragment f = Fragment.instantiate(this, fragmentClass, args);
        if (resultTo != null) {
            f.setTargetFragment(resultTo, resultRequestCode);
@@ -1373,7 +1387,6 @@ public abstract class PreferenceActivity extends ListActivity implements
        transaction.addToBackStack(BACK_STACK_PREFS);
        transaction.commitAllowingStateLoss();
    }
    }

    /**
     * Called by a preference panel fragment to finish itself.
@@ -1385,11 +1398,7 @@ public abstract class PreferenceActivity extends ListActivity implements
     * launching fragment.
     */
    public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
        if (mSinglePane) {
            setResult(resultCode, resultData);
            finish();
        } else {
            // XXX be smarter about popping the stack.
        // TODO: be smarter about popping the stack.
        onBackPressed();
        if (caller != null) {
            if (caller.getTargetFragment() != null) {
@@ -1398,7 +1407,6 @@ public abstract class PreferenceActivity extends ListActivity implements
            }
        }
    }
    }

    @Override
    public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
+2 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
    android:layout_width="match_parent">

    <LinearLayout
        android:id="@+id/prefs_container"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="0px"
@@ -61,8 +62,7 @@
                android:layout_width="0px"
                android:layout_height="match_parent"
                android:layout_weight="@integer/preferences_right_pane_weight"
                android:orientation="vertical"
                android:visibility="gone" >
                android:orientation="vertical">

            <!-- Breadcrumb inserted here, in certain screen sizes. In others, it will be an
                empty layout or just padding, and PreferenceActivity will put the breadcrumbs in
+2 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
    android:layout_width="match_parent">

    <LinearLayout
        android:id="@+id/prefs_container"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="0px"
@@ -64,8 +65,7 @@
                android:layout_width="0px"
                android:layout_height="match_parent"
                android:layout_weight="@integer/preferences_right_pane_weight"
                android:orientation="vertical"
                android:visibility="gone" >
                android:orientation="vertical">

            <!-- Breadcrumb inserted here, in certain screen sizes. In others, it will be an
                empty layout or just padding, and PreferenceActivity will put the breadcrumbs in
+1 −0
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@
  <java-symbol type="id" name="permission_list" />
  <java-symbol type="id" name="pickers" />
  <java-symbol type="id" name="prefs" />
  <java-symbol type="id" name="prefs_container" />
  <java-symbol type="id" name="prefs_frame" />
  <java-symbol type="id" name="prev" />
  <java-symbol type="id" name="progress" />