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

Commit 3a57fb9e authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix issue #3191573: PreferenceFragment.onActivityResult cannot

launch a new fragment on the same call.

There were some problems with the API design where you could do
things in such a way that a back stack entry that was not at the
top would get popped.  Ouch.  Hopefully this change prevents that
from being able to happen.

Change-Id: I8cbc952e12ddd231ff6c84b6e9bbf5125f449f04
parent 248caac2
Loading
Loading
Loading
Loading
+54 −54
Original line number Diff line number Diff line
@@ -21911,47 +21911,6 @@
<parameter name="exitAnim" type="int">
</parameter>
</method>
<method name="popBackStack"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="deprecated"
 visibility="public"
>
</method>
<method name="popBackStack"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="deprecated"
 visibility="public"
>
<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="flags" type="int">
</parameter>
</method>
<method name="popBackStack"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="deprecated"
 visibility="public"
>
<parameter name="id" type="int">
</parameter>
<parameter name="flags" type="int">
</parameter>
</method>
<method name="registerForContextMenu"
 return="void"
 abstract="false"
@@ -22637,17 +22596,6 @@
 visibility="protected"
>
</field>
<field name="POP_BACK_STACK_INCLUSIVE"
 type="int"
 transient="false"
 volatile="false"
 value="1"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="RESULT_CANCELED"
 type="int"
 transient="false"
@@ -28360,6 +28308,17 @@
<parameter name="args" type="java.lang.String[]">
</parameter>
</method>
<method name="executePendingTransactions"
 return="boolean"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="findFragmentById"
 return="android.app.Fragment"
 abstract="true"
@@ -28426,7 +28385,7 @@
>
</method>
<method name="popBackStack"
 return="boolean"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
@@ -28437,7 +28396,7 @@
>
</method>
<method name="popBackStack"
 return="boolean"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
@@ -28452,6 +28411,47 @@
</parameter>
</method>
<method name="popBackStack"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="id" type="int">
</parameter>
<parameter name="flags" type="int">
</parameter>
</method>
<method name="popBackStackImmediate"
 return="boolean"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="popBackStackImmediate"
 return="boolean"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="flags" type="int">
</parameter>
</method>
<method name="popBackStackImmediate"
 return="boolean"
 abstract="true"
 native="false"
+1 −52
Original line number Diff line number Diff line
@@ -2085,64 +2085,13 @@ public class Activity extends ContextThemeWrapper
        return false;
    }
    
    /**
     * Flag for {@link #popBackStack(String, int)}
     * and {@link #popBackStack(int, int)}: If set, and the name or ID of
     * a back stack entry has been supplied, then all matching entries will
     * be consumed until one that doesn't match is found or the bottom of
     * the stack is reached.  Otherwise, all entries up to but not including that entry
     * will be removed.
     */
    public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;

    /**
     * Pop the top state off the back stack.  Returns true if there was one
     * to pop, else false.
     * @deprecated use {@link #getFragmentManager}.
     */
    @Deprecated
    public boolean popBackStack() {
        return mFragments.popBackStack();
    }

    /**
     * Pop the last fragment transition from the local activity's fragment
     * back stack.  If there is nothing to pop, false is returned.
     * @param name If non-null, this is the name of a previous back state
     * to look for; if found, all states up to that state will be popped.  The
     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
     * the named state itself is popped. If null, only the top state is popped.
     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
     * @deprecated use {@link #getFragmentManager}.
     */
    @Deprecated
    public boolean popBackStack(String name, int flags) {
        return mFragments.popBackStack(name, flags);
    }

    /**
     * Pop all back stack states up to the one with the given identifier.
     * @param id Identifier of the stated to be popped. If no identifier exists,
     * false is returned.
     * The identifier is the number returned by
     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.  The
     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
     * the named state itself is popped.
     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
     * @deprecated use {@link #getFragmentManager}.
     */
    @Deprecated
    public boolean popBackStack(int id, int flags) {
        return mFragments.popBackStack(id, flags);
    }
    
    /**
     * Called when the activity has detected the user's press of the back
     * key.  The default implementation simply finishes the current activity,
     * but you can override this to do whatever you want.
     */
    public void onBackPressed() {
        if (!mFragments.popBackStack()) {
        if (!mFragments.popBackStackImmediate()) {
            finish();
        }
    }
+122 −36
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.animation.AnimatorListenerAdapter;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -98,6 +99,20 @@ public abstract class FragmentManager {
     */
    public abstract FragmentTransaction openTransaction();

    /**
     * After a {@link FragmentTransaction} is committed with
     * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it
     * is scheduled to be executed asynchronously on the process's main thread.
     * If you want to immediately executing any such pending operations, you
     * can call this function (only from the main thread) to do so.  Note that
     * all callbacks and other related behavior will be done from within this
     * call, so be careful about where this is called from.
     *
     * @return Returns true if there were any pending transactions to be
     * executed.
     */
    public abstract boolean executePendingTransactions();

    /**
     * Finds a fragment that was identified by the given id either when inflated
     * from XML or as the container ID when added in a transaction.  This first
@@ -132,7 +147,15 @@ public abstract class FragmentManager {
     * Pop the top state off the back stack.  Returns true if there was one
     * to pop, else false.
     */
    public abstract boolean popBackStack();
    public abstract void popBackStack();

    /**
     * Like {@link #popBackStack()}, but performs the operation immediately
     * inside of the call.  This is like calling {@link #executePendingTransactions()}
     * afterwards.
     * @return Returns true if there was something popped, else false.
     */
    public abstract boolean popBackStackImmediate();

    /**
     * Pop the last fragment transition from the manager's fragment
@@ -143,7 +166,15 @@ public abstract class FragmentManager {
     * the named state itself is popped. If null, only the top state is popped.
     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
     */
    public abstract boolean popBackStack(String name, int flags);
    public abstract void popBackStack(String name, int flags);

    /**
     * Like {@link #popBackStack(String, int)}, but performs the operation immediately
     * inside of the call.  This is like calling {@link #executePendingTransactions()}
     * afterwards.
     * @return Returns true if there was something popped, else false.
     */
    public abstract boolean popBackStackImmediate(String name, int flags);

    /**
     * Pop all back stack states up to the one with the given identifier.
@@ -155,7 +186,15 @@ public abstract class FragmentManager {
     * the named state itself is popped.
     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
     */
    public abstract boolean popBackStack(int id, int flags);
    public abstract void popBackStack(int id, int flags);

    /**
     * Like {@link #popBackStack(int, int)}, but performs the operation immediately
     * inside of the call.  This is like calling {@link #executePendingTransactions()}
     * afterwards.
     * @return Returns true if there was something popped, else false.
     */
    public abstract boolean popBackStackImmediate(int id, int flags);

    /**
     * Return the number of entries currently in the back stack.
@@ -300,17 +339,58 @@ final class FragmentManagerImpl extends FragmentManager {
    }

    @Override
    public boolean popBackStack() {
    public boolean executePendingTransactions() {
        return execPendingActions();
    }

    @Override
    public void popBackStack() {
        enqueueAction(new Runnable() {
            @Override public void run() {
                popBackStackState(mActivity.mHandler, null, -1, 0);
            }
        }, false);
    }

    @Override
    public boolean popBackStackImmediate() {
        checkStateLoss();
        executePendingTransactions();
        return popBackStackState(mActivity.mHandler, null, -1, 0);
    }

    @Override
    public boolean popBackStack(String name, int flags) {
    public void popBackStack(final String name, final int flags) {
        enqueueAction(new Runnable() {
            @Override public void run() {
                popBackStackState(mActivity.mHandler, name, -1, flags);
            }
        }, false);
    }

    @Override
    public boolean popBackStackImmediate(String name, int flags) {
        checkStateLoss();
        executePendingTransactions();
        return popBackStackState(mActivity.mHandler, name, -1, flags);
    }

    @Override
    public boolean popBackStack(int id, int flags) {
    public void popBackStack(final int id, final int flags) {
        if (id < 0) {
            throw new IllegalArgumentException("Bad id: " + id);
        }
        enqueueAction(new Runnable() {
            @Override public void run() {
                popBackStackState(mActivity.mHandler, null, id, flags);
            }
        }, false);
    }

    @Override
    public boolean popBackStackImmediate(int id, int flags) {
        checkStateLoss();
        executePendingTransactions();
        if (id < 0) {
            throw new IllegalArgumentException("Bad id: " + id);
        }
@@ -849,8 +929,7 @@ final class FragmentManagerImpl extends FragmentManager {
        return null;
    }
    
    public void enqueueAction(Runnable action, boolean allowStateLoss) {
        if (!allowStateLoss) {
    private void checkStateLoss() {
        if (mStateSaved) {
            throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
@@ -860,6 +939,11 @@ final class FragmentManagerImpl extends FragmentManager {
                    "Can not perform this action inside of " + mNoTransactionsBecause);
        }
    }

    public void enqueueAction(Runnable action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mActivity == null) {
                throw new IllegalStateException("Activity has been destroyed");
@@ -934,17 +1018,23 @@ final class FragmentManagerImpl extends FragmentManager {
    /**
     * Only call from main thread!
     */
    public void execPendingActions() {
    public boolean execPendingActions() {
        if (mExecutingActions) {
            throw new IllegalStateException("Recursive entry to execPendingActions");
            throw new IllegalStateException("Recursive entry to executePendingTransactions");
        }
        
        if (Looper.myLooper() != Looper.getMainLooper()) {
            throw new IllegalStateException("Must be called from main thread of process");
        }

        boolean didSomething = false;

        while (true) {
            int numActions;
            
            synchronized (this) {
                if (mPendingActions == null || mPendingActions.size() == 0) {
                    return;
                    return didSomething;
                }
                
                numActions = mPendingActions.size();
@@ -961,6 +1051,7 @@ final class FragmentManagerImpl extends FragmentManager {
                mTmpActions[i].run();
            }
            mExecutingActions = false;
            didSomething = true;
        }
    }
    
@@ -984,19 +1075,14 @@ final class FragmentManagerImpl extends FragmentManager {
        if (mBackStack == null) {
            return false;
        }
        if (name == null && id < 0 && (flags&Activity.POP_BACK_STACK_INCLUSIVE) == 0) {
        if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
            int last = mBackStack.size()-1;
            if (last < 0) {
                return false;
            }
            final BackStackRecord bss = mBackStack.remove(last);
            enqueueAction(new Runnable() {
                public void run() {
                    if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
            bss.popFromBackStack(true);
            reportBackStackChanged();
                }
            }, false);
        } else {
            int index = -1;
            if (name != null || id >= 0) {
@@ -1016,7 +1102,7 @@ final class FragmentManagerImpl extends FragmentManager {
                if (index < 0) {
                    return false;
                }
                if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) {
                if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
                    index--;
                    // Consume all following entries that match.
                    while (index >= 0) {
@@ -1038,8 +1124,6 @@ final class FragmentManagerImpl extends FragmentManager {
            for (int i=mBackStack.size()-1; i>index; i--) {
                states.add(mBackStack.remove(i));
            }
            enqueueAction(new Runnable() {
                public void run() {
            final int LAST = states.size()-1;
            for (int i=0; i<=LAST; i++) {
                if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
@@ -1047,8 +1131,6 @@ final class FragmentManagerImpl extends FragmentManager {
            }
            reportBackStackChanged();
        }
            }, false);
        }
        return true;
    }
    
@@ -1084,6 +1166,10 @@ final class FragmentManagerImpl extends FragmentManager {
    }
    
    Parcelable saveAllState() {
        // Make sure all pending operations have now been executed to get
        // our state update-to-date.
        execPendingActions();

        mStateSaved = true;

        if (mActive == null || mActive.size() <= 0) {
+7 −4
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParserException;

import android.app.Fragment;
import android.app.FragmentBreadCrumbs;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ListActivity;
import android.content.Context;
@@ -902,7 +903,8 @@ public abstract class PreferenceActivity extends ListActivity implements
    }

    private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
        getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
        getFragmentManager().popBackStack(BACK_STACK_PREFS,
                FragmentManager.POP_BACK_STACK_INCLUSIVE);
        Fragment f = Fragment.instantiate(this, fragmentName, args);
        FragmentTransaction transaction = getFragmentManager().openTransaction();
        transaction.setTransition(direction == 0 ? FragmentTransaction.TRANSIT_NONE
@@ -934,7 +936,8 @@ public abstract class PreferenceActivity extends ListActivity implements
        if (mCurHeader == header) {
            // This is the header we are currently displaying.  Just make sure
            // to pop the stack up to its root state.
            getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
            getFragmentManager().popBackStack(BACK_STACK_PREFS,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
            switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
@@ -1061,14 +1064,14 @@ public abstract class PreferenceActivity extends ListActivity implements
            setResult(resultCode, resultData);
            finish();
        } else {
            // XXX be smarter about popping the stack.
            onBackPressed();
            if (caller != null) {
                if (caller.getTargetFragment() != null) {
                    caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(),
                            resultCode, resultData);
                }
            }
            // XXX be smarter about popping the stack.
            onBackPressed();
        }
    }