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

Commit def1537e authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

More fragment work:

- Introduce FragmentManager as a public API, deprecating the fragment
  APIs on Activity.  (They will be removed soon.)
- Add APIs to write a fragment reference to a bundle and later retrieve
  it.
- Add Fragment API to set another fragment as its target, for delivering
  results.
- Change when onInflate() is called and formalize its meaning in relation
  to the fragment arguments that were previously introduced.
- Change onDestroyView() to always be called, regardless of when
  onCreateView() returns.  It now also is called slightly differently,
  after the view hierarchy's state is saved.
- Fix some issues with DialogFragment's lifecycle with its associated
  Dialog and state save/restore.
- Preference can now have a Bundle associated with it to provide
  arguments to a fragment.  The data for this Bundle call be supplied
  via <extra> tags under a PreferenceScreen.
- PreferenceActivity's header XML tags are now <preference-headers>
  and <header>, and you can supply <extra> tags under a <header> to set
  arguments for the header's fragment.

Change-Id: I22c212c9fa862d50840201ca16e51f9de5ef0031
parent 270f46d9
Loading
Loading
Loading
Loading
+66 −9
Original line number Diff line number Diff line
@@ -21796,7 +21796,7 @@
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 deprecated="deprecated"
 visibility="public"
>
<parameter name="id" type="int">
@@ -21809,7 +21809,7 @@
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 deprecated="deprecated"
 visibility="public"
>
<parameter name="tag" type="java.lang.String">
@@ -23006,7 +23006,7 @@
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 deprecated="deprecated"
 visibility="public"
>
</method>
@@ -23043,7 +23043,7 @@
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 deprecated="deprecated"
 visibility="public"
>
</method>
@@ -23054,7 +23054,7 @@
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 deprecated="deprecated"
 visibility="public"
>
<parameter name="name" type="java.lang.String">
@@ -23069,7 +23069,7 @@
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 deprecated="deprecated"
 visibility="public"
>
<parameter name="id" type="int">
@@ -27856,6 +27856,28 @@
 visibility="public"
>
</method>
<method name="getTargetFragment"
 return="android.app.Fragment"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getTargetRequestCode"
 return="int"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getView"
 return="android.view.View"
 abstract="false"
@@ -28158,8 +28180,6 @@
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="activity" type="android.app.Activity">
</parameter>
<parameter name="attrs" type="android.util.AttributeSet">
</parameter>
<parameter name="savedInstanceState" type="android.os.Bundle">
@@ -28291,7 +28311,7 @@
 native="false"
 synchronized="false"
 static="false"
 final="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
@@ -28324,6 +28344,21 @@
<parameter name="retain" type="boolean">
</parameter>
</method>
<method name="setTargetFragment"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="fragment" type="android.app.Fragment">
</parameter>
<parameter name="requestCode" type="int">
</parameter>
</method>
<method name="startActivity"
 return="void"
 abstract="false"
@@ -137652,6 +137687,17 @@
 visibility="public"
>
</method>
<method name="getExtras"
 return="android.os.Bundle"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getFragment"
 return="java.lang.String"
 abstract="false"
@@ -138095,6 +138141,17 @@
<parameter name="defaultValue" type="java.lang.Object">
</parameter>
</method>
<method name="peekExtras"
 return="android.os.Bundle"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="persistBoolean"
 return="boolean"
 abstract="false"
+23 −8
Original line number Diff line number Diff line
@@ -1590,7 +1590,9 @@ public class Activity extends ContextThemeWrapper
    /**
     * Start a series of edit operations on the Fragments associated with
     * this activity.
     * @deprecated use {@link #getFragmentManager}.
     */
    @Deprecated
    public FragmentTransaction openFragmentTransaction() {
        return mFragments.openTransaction();
    }
@@ -1782,7 +1784,9 @@ public class Activity extends ContextThemeWrapper
     * from XML or as the container ID when added in a transaction.  This only
     * returns fragments that are currently added to the activity's content.
     * @return The fragment if found or null otherwise.
     * @deprecated use {@link #getFragmentManager}.
     */
    @Deprecated
    public Fragment findFragmentById(int id) {
        return mFragments.findFragmentById(id);
    }
@@ -1792,7 +1796,9 @@ public class Activity extends ContextThemeWrapper
     * from XML or as supplied when added in a transaction.  This only
     * returns fragments that are currently added to the activity's content.
     * @return The fragment if found or null otherwise.
     * @deprecated use {@link #getFragmentManager}.
     */
    @Deprecated
    public Fragment findFragmentByTag(String tag) {
        return mFragments.findFragmentByTag(tag);
    }
@@ -2078,7 +2084,9 @@ public class Activity extends ContextThemeWrapper
    /**
     * 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();
    }
@@ -2091,7 +2099,9 @@ public class Activity extends ContextThemeWrapper
     * {@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);
    }
@@ -2105,7 +2115,9 @@ public class Activity extends ContextThemeWrapper
     * {@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);
    }
@@ -2116,7 +2128,7 @@ public class Activity extends ContextThemeWrapper
     * but you can override this to do whatever you want.
     */
    public void onBackPressed() {
        if (!popBackStack()) {
        if (!mFragments.popBackStack()) {
            finish();
        }
    }
@@ -3995,9 +4007,12 @@ public class Activity extends ContextThemeWrapper
            return null;
        }
        
        String fname = attrs.getAttributeValue(null, "class");
        TypedArray a = 
            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
        String fname = a.getString(com.android.internal.R.styleable.Fragment_name);
        if (fname == null) {
            fname = a.getString(com.android.internal.R.styleable.Fragment_name);
        }
        int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, 0);
        String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
        a.recycle();
@@ -4020,13 +4035,13 @@ public class Activity extends ContextThemeWrapper
            fragment.mFragmentId = id;
            fragment.mTag = tag;
            fragment.mImmediateActivity = this;
            mFragments.addFragment(fragment, true);
        }
            // If this fragment is newly instantiated (either right now, or
            // from last saved state), then give it the attributes to
            // initialize itself.
            if (!fragment.mRetaining) {
            fragment.onInflate(this, attrs, fragment.mSavedFragmentState);
                fragment.onInflate(attrs, fragment.mSavedFragmentState);
            }
            mFragments.addFragment(fragment, true);
        }
        if (fragment.mView == null) {
            throw new IllegalStateException("Fragment " + fname
+26 −13
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ public class DialogFragment extends Fragment

    Dialog mDialog;
    boolean mDestroyed;
    boolean mRemoved;

    public DialogFragment() {
    }
@@ -136,6 +137,7 @@ public class DialogFragment extends Fragment
     */
    public int show(Activity activity, FragmentTransaction transaction, String tag) {
        transaction.add(this, tag);
        mRemoved = false;
        mBackStackId = transaction.commit();
        return mBackStackId;
    }
@@ -149,12 +151,15 @@ public class DialogFragment extends Fragment
    public void dismiss() {
        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
        }
        mRemoved = true;
        if (mBackStackId >= 0) {
            getActivity().popBackStack(mBackStackId, Activity.POP_BACK_STACK_INCLUSIVE);
            getFragmentManager().popBackStack(mBackStackId,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            mBackStackId = -1;
        } else {
            FragmentTransaction ft = getActivity().openFragmentTransaction();
            FragmentTransaction ft = getFragmentManager().openTransaction();
            ft.remove(this);
            ft.commit();
        }
@@ -193,15 +198,12 @@ public class DialogFragment extends Fragment
    }

    public void onCancel(DialogInterface dialog) {
        if (mBackStackId >= 0) {
            // If this fragment is part of the back stack, then cancelling
            // the dialog means popping off the back stack.
            getActivity().popBackStack(mBackStackId, Activity.POP_BACK_STACK_INCLUSIVE);
            mBackStackId = -1;
        }
    }

    public void onDismiss(DialogInterface dialog) {
        if (!mRemoved) {
            dismiss();
        }
    }

    @Override
@@ -241,8 +243,11 @@ public class DialogFragment extends Fragment
    @Override
    public void onStart() {
        super.onStart();
        if (mDialog != null) {
            mRemoved = false;
            mDialog.show();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
@@ -262,17 +267,25 @@ public class DialogFragment extends Fragment
    @Override
    public void onStop() {
        super.onStop();
        if (mDialog != null) {
            mDialog.hide();
        }
    }

    /**
     * Detach from list view.
     * Remove dialog.
     */
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mDestroyed = true;
        if (mDialog != null) {
            // Set removed here because this dismissal is just to hide
            // the dialog -- we don't want this to cause the fragment to
            // actually be removed.
            mRemoved = true;
            mDialog.dismiss();
            mDialog = null;
        }
    }
}
+58 −15
Original line number Diff line number Diff line
@@ -42,8 +42,6 @@ import android.widget.AdapterView;
import java.util.HashMap;

final class FragmentState implements Parcelable {
    static final String VIEW_STATE_TAG = "android:view_state";
    
    final String mClassName;
    final int mIndex;
    final boolean mFromLayout;
@@ -90,8 +88,6 @@ final class FragmentState implements Parcelable {
        if (mSavedFragmentState != null) {
            mSavedFragmentState.setClassLoader(activity.getClassLoader());
            mInstance.mSavedFragmentState = mSavedFragmentState;
            mInstance.mSavedViewState
                    = mSavedFragmentState.getSparseParcelableArray(VIEW_STATE_TAG);
        }
        mInstance.setIndex(mIndex);
        mInstance.mFromLayout = mFromLayout;
@@ -160,6 +156,12 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
    // Construction arguments;
    Bundle mArguments;

    // Target fragment.
    Fragment mTarget;

    // Target request code.
    int mTargetRequestCode;

    // True if the fragment is in the list of added fragments.
    boolean mAdded;
    
@@ -375,7 +377,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
     * arguments supplied here will be retained across fragment destroy and
     * creation.
     */
    final public void setArguments(Bundle args) {
    public void setArguments(Bundle args) {
        if (mIndex >= 0) {
            throw new IllegalStateException("Fragment already active");
        }
@@ -390,6 +392,36 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
        return mArguments;
    }

    /**
     * Optional target for this fragment.  This may be used, for example,
     * if this fragment is being started by another, and when done wants to
     * give a result back to the first.  The target set here is retained
     * across instances via {@link FragmentManager#putFragment
     * FragmentManager.putFragment()}.
     *
     * @param fragment The fragment that is the target of this one.
     * @param requestCode Optional request code, for convenience if you
     * are going to call back with {@link #onActivityResult(int, int, Intent)}.
     */
    public void setTargetFragment(Fragment fragment, int requestCode) {
        mTarget = fragment;
        mTargetRequestCode = requestCode;
    }

    /**
     * Return the target fragment set by {@link #setTargetFragment(Fragment)}.
     */
    final public Fragment getTargetFragment() {
        return mTarget;
    }

    /**
     * Return the target request code set by {@link #setTargetFragment(Fragment)}.
     */
    final public int getTargetRequestCode() {
        return mTargetRequestCode;
    }

    /**
     * Return the Activity this fragment is currently associated with.
     */
@@ -537,21 +569,30 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
    /**
     * Called when a fragment is being created as part of a view layout
     * inflation, typically from setting the content view of an activity.  This
     * will be called both the first time the fragment is created, as well
     * later when it is being re-created from its saved state (which is also
     * given here).
     * will be called immediately after the fragment is created from a <fragment>
     * tag in a layout file.  Note this is <em>before</em> the fragment's
     * {@link #onAttach(Activity)} has been called; all you should do here is
     * parse the attributes and save them away.  A convenient thing to do is
     * simply copy them into a Bundle that is given to {@link #setArguments(Bundle)}.
     * 
     * XXX This is kind-of yucky...  maybe we could just supply the
     * AttributeSet to onCreate()?
     * <p>This is called every time the fragment is inflated, even if it is
     * being inflated into a new instance with saved state.  Because a fragment's
     * arguments are retained across instances, it may make no sense to re-parse
     * the attributes into new arguments.  You may want to first check
     * {@link #getArguments()} and only parse the attributes if it returns null,
     * the assumption being that if it is non-null those are the same arguments
     * from the first time the fragment was inflated.  (That said, you may want
     * to have layouts change for different configurations such as landscape
     * and portrait, which can have different attributes.  If so, you will need
     * to re-parse the attributes each time this is called to generate new
     * arguments.)</p>
     * 
     * @param activity The Activity that is inflating the fragment.
     * @param attrs The attributes at the tag where the fragment is
     * being created.
     * @param savedInstanceState If the fragment is being re-created from
     * a previous saved state, this is the state.
     */
    public void onInflate(Activity activity, AttributeSet attrs,
            Bundle savedInstanceState) {
    public void onInflate(AttributeSet attrs, Bundle savedInstanceState) {
        mCalled = true;
    }
    
@@ -693,8 +734,10 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
     * Called when the view previously created by {@link #onCreateView} has
     * been detached from the fragment.  The next time the fragment needs
     * to be displayed, a new view will be created.  This is called
     * after {@link #onStop()} and before {@link #onDestroy()}; it is only
     * called if {@link #onCreateView} returns a non-null View.
     * after {@link #onStop()} and before {@link #onDestroy()}.  It is called
     * <em>regardless</em> of whether {@link #onCreateView} returned a
     * non-null view.  Internally it is called after the view's state has
     * been saved but before it has been removed from its parent.
     */
    public void onDestroyView() {
        mCalled = true;
+52 −24
Original line number Diff line number Diff line
@@ -166,13 +166,16 @@ final class FragmentManagerState implements Parcelable {
}

/**
 * @hide
 * Container for fragments associated with an activity.
 */
class FragmentManagerImpl implements FragmentManager {
final class FragmentManagerImpl implements FragmentManager {
    static final boolean DEBUG = true;
    static final String TAG = "FragmentManager";
    
    static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
    static final String TARGET_STATE_TAG = "android:target_state";
    static final String VIEW_STATE_TAG = "android:view_state";

    ArrayList<Runnable> mPendingActions;
    Runnable[] mTmpActions;
    boolean mExecutingActions;
@@ -201,7 +204,6 @@ class FragmentManagerImpl implements FragmentManager {
            execPendingActions();
        }
    };
    
    public FragmentTransaction openTransaction() {
        return new BackStackEntry(this);
    }
@@ -230,7 +232,10 @@ class FragmentManagerImpl implements FragmentManager {
    }

    public Fragment getFragment(Bundle bundle, String key) {
        int index = bundle.getInt(key);
        int index = bundle.getInt(key, -1);
        if (index == -1) {
            return null;
        }
        if (index >= mActive.size()) {
            throw new IllegalStateException("Fragement no longer exists for key "
                    + key + ": index " + index);
@@ -296,6 +301,16 @@ class FragmentManagerImpl implements FragmentManager {
            switch (f.mState) {
                case Fragment.INITIALIZING:
                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                    if (f.mSavedFragmentState != null) {
                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                                FragmentManagerImpl.VIEW_STATE_TAG);
                        f.mTarget = getFragment(f.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG);
                        if (f.mTarget != null) {
                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                        }
                    }
                    f.mActivity = mActivity;
                    f.mCalled = false;
                    f.onAttach(mActivity);
@@ -419,18 +434,19 @@ class FragmentManagerImpl implements FragmentManager {
                    if (newState < Fragment.ACTIVITY_CREATED) {
                        if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
                        if (f.mView != null) {
                            // Need to save the current view state if not
                            // done already.
                            if (!mActivity.isFinishing() && f.mSavedFragmentState == null) {
                                saveFragmentViewState(f);
                            }
                        }
                        f.mCalled = false;
                        f.onDestroyView();
                        if (!f.mCalled) {
                            throw new SuperNotCalledException("Fragment " + f
                                    + " did not call through to super.onDestroyedView()");
                        }
                            // Need to save the current view state if not
                            // done already.
                            if (!mActivity.isFinishing() && f.mSavedFragmentState == null) {
                                saveFragmentViewState(f);
                            }
                            if (f.mContainer != null) {
                        if (f.mView != null && f.mContainer != null) {
                            if (mCurState > Fragment.INITIALIZING) {
                                Animatable anim = loadAnimatable(f, transit, true,
                                        transitionStyle);
@@ -445,7 +461,6 @@ class FragmentManagerImpl implements FragmentManager {
                            }
                            f.mContainer.removeView(f.mView);
                        }
                        }
                        f.mContainer = null;
                        f.mView = null;
                    }
@@ -908,7 +923,20 @@ class FragmentManagerImpl implements FragmentManager {
                            fs.mSavedFragmentState = new Bundle();
                        }
                        fs.mSavedFragmentState.putSparseParcelableArray(
                                FragmentState.VIEW_STATE_TAG, f.mSavedViewState);
                                FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
                    }
                }

                if (f.mTarget != null) {
                    if (fs.mSavedFragmentState == null) {
                        fs.mSavedFragmentState = new Bundle();
                    }
                    putFragment(fs.mSavedFragmentState,
                            FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
                    if (f.mTargetRequestCode != 0) {
                        fs.mSavedFragmentState.putInt(
                                FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
                                f.mTargetRequestCode);
                    }
                }
                
@@ -976,7 +1004,7 @@ class FragmentManagerImpl implements FragmentManager {
                f.mAdded = false;
                if (fs.mSavedFragmentState != null) {
                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
                            FragmentState.VIEW_STATE_TAG);
                            FragmentManagerImpl.VIEW_STATE_TAG);
                }
            }
        }
Loading