Fragment.java revision 5ae74d6e89a30e79ea85c487b32223ef55314985
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import android.content.ComponentCallbacks;
20import android.content.Intent;
21import android.content.res.Configuration;
22import android.os.Bundle;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.util.AttributeSet;
26import android.util.SparseArray;
27import android.view.LayoutInflater;
28import android.view.View;
29import android.view.ViewGroup;
30import android.view.animation.Animation;
31
32import java.lang.reflect.InvocationTargetException;
33import java.util.HashMap;
34
35final class FragmentState implements Parcelable {
36    static final String VIEW_STATE_TAG = "android:view_state";
37
38    final String mClassName;
39    final int mIndex;
40    final boolean mFromLayout;
41    final int mFragmentId;
42    final int mContainerId;
43    final String mTag;
44    final boolean mRetainInstance;
45
46    Bundle mSavedFragmentState;
47
48    Fragment mInstance;
49
50    public FragmentState(Fragment frag) {
51        mClassName = frag.getClass().getName();
52        mIndex = frag.mIndex;
53        mFromLayout = frag.mFromLayout;
54        mFragmentId = frag.mFragmentId;
55        mContainerId = frag.mContainerId;
56        mTag = frag.mTag;
57        mRetainInstance = frag.mRetainInstance;
58    }
59
60    public FragmentState(Parcel in) {
61        mClassName = in.readString();
62        mIndex = in.readInt();
63        mFromLayout = in.readInt() != 0;
64        mFragmentId = in.readInt();
65        mContainerId = in.readInt();
66        mTag = in.readString();
67        mRetainInstance = in.readInt() != 0;
68        mSavedFragmentState = in.readBundle();
69    }
70
71    public Fragment instantiate(Activity activity) {
72        if (mInstance != null) {
73            return mInstance;
74        }
75
76        try {
77            mInstance = Fragment.instantiate(activity, mClassName);
78        } catch (Exception e) {
79            throw new RuntimeException("Unable to restore fragment " + mClassName, e);
80        }
81
82        if (mSavedFragmentState != null) {
83            mSavedFragmentState.setClassLoader(activity.getClassLoader());
84            mInstance.mSavedFragmentState = mSavedFragmentState;
85            mInstance.mSavedViewState
86                    = mSavedFragmentState.getSparseParcelableArray(VIEW_STATE_TAG);
87        }
88        mInstance.setIndex(mIndex);
89        mInstance.mFromLayout = mFromLayout;
90        mInstance.mFragmentId = mFragmentId;
91        mInstance.mContainerId = mContainerId;
92        mInstance.mTag = mTag;
93        mInstance.mRetainInstance = mRetainInstance;
94
95        return mInstance;
96    }
97
98    public int describeContents() {
99        return 0;
100    }
101
102    public void writeToParcel(Parcel dest, int flags) {
103        dest.writeString(mClassName);
104        dest.writeInt(mIndex);
105        dest.writeInt(mFromLayout ? 1 : 0);
106        dest.writeInt(mFragmentId);
107        dest.writeInt(mContainerId);
108        dest.writeString(mTag);
109        dest.writeInt(mRetainInstance ? 1 : 0);
110        dest.writeBundle(mSavedFragmentState);
111    }
112
113    public static final Parcelable.Creator<FragmentState> CREATOR
114            = new Parcelable.Creator<FragmentState>() {
115        public FragmentState createFromParcel(Parcel in) {
116            return new FragmentState(in);
117        }
118
119        public FragmentState[] newArray(int size) {
120            return new FragmentState[size];
121        }
122    };
123}
124
125/**
126 * A Fragment is a piece of an application's user interface or behavior
127 * that can be placed in an {@link Activity}.
128 */
129public class Fragment implements ComponentCallbacks {
130    private static final HashMap<String, Class> sClassMap =
131            new HashMap<String, Class>();
132
133    static final int INITIALIZING = 0;  // Not yet created.
134    static final int CREATED = 1;       // Created.
135    static final int CONTENT = 2;       // View hierarchy content available.
136    static final int STARTED = 3;       // Created and started, not resumed.
137    static final int RESUMED = 4;       // Created started and resumed.
138
139    int mState = INITIALIZING;
140
141    // When instantiated from saved state, this is the saved state.
142    Bundle mSavedFragmentState;
143    SparseArray<Parcelable> mSavedViewState;
144
145    // Index into active fragment array.
146    int mIndex = -1;
147
148    // Internal unique name for this fragment;
149    String mWho;
150
151    // True if the fragment is in the list of added fragments.
152    boolean mAdded;
153
154    // Set to true if this fragment was instantiated from a layout file.
155    boolean mFromLayout;
156
157    // Number of active back stack entries this fragment is in.
158    int mBackStackNesting;
159
160    // Activity this fragment is attached to.
161    Activity mActivity;
162
163    // The optional identifier for this fragment -- either the container ID if it
164    // was dynamically added to the view hierarchy, or the ID supplied in
165    // layout.
166    int mFragmentId;
167
168    // When a fragment is being dynamically added to the view hierarchy, this
169    // is the identifier of the parent container it is being added to.
170    int mContainerId;
171
172    // The optional named tag for this fragment -- usually used to find
173    // fragments that are not part of the layout.
174    String mTag;
175
176    // Set to true when the app has requested that this fragment be hidden
177    // from the user.
178    boolean mHidden;
179
180    // If set this fragment would like its instance retained across
181    // configuration changes.
182    boolean mRetainInstance;
183
184    // If set this fragment is being retained across the current config change.
185    boolean mRetaining;
186
187    // Used to verify that subclasses call through to super class.
188    boolean mCalled;
189
190    // If app has requested a specific animation, this is the one to use.
191    int mNextAnim;
192
193    // The parent container of the fragment after dynamically added to UI.
194    ViewGroup mContainer;
195
196    // The View generated for this fragment.
197    View mView;
198
199    public Fragment() {
200    }
201
202    static Fragment instantiate(Activity activity, String fname)
203            throws NoSuchMethodException, ClassNotFoundException,
204            IllegalArgumentException, InstantiationException,
205            IllegalAccessException, InvocationTargetException {
206        Class clazz = sClassMap.get(fname);
207
208        if (clazz == null) {
209            // Class not found in the cache, see if it's real, and try to add it
210            clazz = activity.getClassLoader().loadClass(fname);
211            sClassMap.put(fname, clazz);
212        }
213        return (Fragment)clazz.newInstance();
214    }
215
216    void restoreViewState() {
217        if (mSavedViewState != null) {
218            mView.restoreHierarchyState(mSavedViewState);
219            mSavedViewState = null;
220        }
221    }
222
223    void setIndex(int index) {
224        mIndex = index;
225        mWho = "android:fragment:" + mIndex;
226   }
227
228    void clearIndex() {
229        mIndex = -1;
230        mWho = null;
231    }
232
233    /**
234     * Subclasses can not override equals().
235     */
236    @Override final public boolean equals(Object o) {
237        return super.equals(o);
238    }
239
240    /**
241     * Subclasses can not override hashCode().
242     */
243    @Override final public int hashCode() {
244        return super.hashCode();
245    }
246
247    @Override
248    public String toString() {
249        StringBuilder sb = new StringBuilder(128);
250        sb.append("Fragment{");
251        sb.append(Integer.toHexString(System.identityHashCode(this)));
252        if (mIndex >= 0) {
253            sb.append(" #");
254            sb.append(mIndex);
255        }
256        if (mFragmentId != 0) {
257            sb.append(" id=0x");
258            sb.append(Integer.toHexString(mFragmentId));
259        }
260        if (mTag != null) {
261            sb.append(" ");
262            sb.append(mTag);
263        }
264        sb.append('}');
265        return sb.toString();
266    }
267
268    /**
269     * Return the identifier this fragment is known by.  This is either
270     * the android:id value supplied in a layout or the container view ID
271     * supplied when adding the fragment.
272     */
273    public int getId() {
274        return mFragmentId;
275    }
276
277    /**
278     * Get the tag name of the fragment, if specified.
279     */
280    public String getTag() {
281        return mTag;
282    }
283
284    /**
285     * Return the Activity this fragment is currently associated with.
286     */
287    public Activity getActivity() {
288        return mActivity;
289    }
290
291    /**
292     * Return true if the fragment is currently added to its activity.
293     */
294    public boolean isAdded() {
295        return mActivity != null && mActivity.mFragments.mAdded.contains(this);
296    }
297
298    /**
299     * Return true if the fragment is currently visible to the user.  This means
300     * it: (1) has been added, (2) has its view attached to the window, and
301     * (3) is not hidden.
302     */
303    public boolean isVisible() {
304        return isAdded() && !isHidden() && mView != null
305                && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
306    }
307
308    /**
309     * Return true if the fragment has been hidden.  By default fragments
310     * are shown.  You can find out about changes to this state with
311     * {@link #onHiddenChanged()}.  Note that the hidden state is orthogonal
312     * to other states -- that is, to be visible to the user, a fragment
313     * must be both started and not hidden.
314     */
315    public boolean isHidden() {
316        return mHidden;
317    }
318
319    /**
320     * Called when the hidden state (as returned by {@link #isHidden()} of
321     * the fragment has changed.  Fragments start out not hidden; this will
322     * be called whenever the fragment changes state from that.
323     * @param hidden True if the fragment is now hidden, false if it is not
324     * visible.
325     */
326    public void onHiddenChanged(boolean hidden) {
327    }
328
329    /**
330     * Control whether a fragment instance is retained across Activity
331     * re-creation (such as from a configuration change).  This can only
332     * be used with fragments not in the back stack.  If set, the fragment
333     * lifecycle will be slightly different when an activity is recreated:
334     * <ul>
335     * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
336     * will be, because the fragment is being detached from its current activity).
337     * <li> {@link #onCreate(Bundle)} will not be called since the fragment
338     * is not being re-created.
339     * <li> {@link #onAttach(Activity)} and {@link #onReady(Bundle)} <b>will</b>
340     * still be called.
341     * </ul>
342     */
343    public void setRetainInstance(boolean retain) {
344        mRetainInstance = retain;
345    }
346
347    public boolean getRetainInstance() {
348        return mRetainInstance;
349    }
350
351    /**
352     * Call {@link Activity#startActivity(Intent)} on the fragment's
353     * containing Activity.
354     */
355    public void startActivity(Intent intent) {
356        mActivity.startActivityFromFragment(this, intent, -1);
357    }
358
359    /**
360     * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's
361     * containing Activity.
362     */
363    public void startActivityForResult(Intent intent, int requestCode) {
364        mActivity.startActivityFromFragment(this, intent, requestCode);
365    }
366
367    /**
368     * Receive the result from a previous call to
369     * {@link #startActivityForResult(Intent, int)}.  This follows the
370     * related Activity API as described there in
371     * {@link Activity#onActivityResult(int, int, Intent)}.
372     *
373     * @param requestCode The integer request code originally supplied to
374     *                    startActivityForResult(), allowing you to identify who this
375     *                    result came from.
376     * @param resultCode The integer result code returned by the child activity
377     *                   through its setResult().
378     * @param data An Intent, which can return result data to the caller
379     *               (various data can be attached to Intent "extras").
380     */
381    public void onActivityResult(int requestCode, int resultCode, Intent data) {
382    }
383
384    /**
385     * Called when a fragment is being created as part of a view layout
386     * inflation, typically from setting the content view of an activity.  This
387     * will be called both the first time the fragment is created, as well
388     * later when it is being re-created from its saved state (which is also
389     * given here).
390     *
391     * XXX This is kind-of yucky...  maybe we could just supply the
392     * AttributeSet to onCreate()?
393     *
394     * @param activity The Activity that is inflating the fragment.
395     * @param attrs The attributes at the tag where the fragment is
396     * being created.
397     * @param savedInstanceState If the fragment is being re-created from
398     * a previous saved state, this is the state.
399     */
400    public void onInflate(Activity activity, AttributeSet attrs,
401            Bundle savedInstanceState) {
402        mCalled = true;
403    }
404
405    /**
406     * Called when a fragment is first attached to its activity.
407     * {@link #onCreate(Bundle)} will be called after this.
408     */
409    public void onAttach(Activity activity) {
410        mCalled = true;
411    }
412
413    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
414        return null;
415    }
416
417    /**
418     * Called to do initial creation of a fragment.  This is called after
419     * {@link #onAttach(Activity)} and before {@link #onReady(Bundle)}.
420     * @param savedInstanceState If the fragment is being re-created from
421     * a previous saved state, this is the state.
422     */
423    public void onCreate(Bundle savedInstanceState) {
424        mCalled = true;
425    }
426
427    /**
428     * Called to have the fragment instantiate its user interface view.
429     * This is optional, and non-graphical fragments can return null (which
430     * is the default implementation).  This will be called between
431     * {@link #onCreate(Bundle)} and {@link #onReady(Bundle)}.
432     *
433     * @param inflater The LayoutInflater object that can be used to inflate
434     * any views in the fragment,
435     * @param container If non-null, this is the parent view that the fragment's
436     * UI should be attached to.  The fragment should not add the view itself,
437     * but this can be used to generate the LayoutParams of the view.
438     * @param savedInstanceState If non-null, this fragment is being re-constructed
439     * from a previous saved state as given here.
440     *
441     * @return Return the View for the fragment's UI, or null.
442     */
443    public View onCreateView(LayoutInflater inflater, ViewGroup container,
444            Bundle savedInstanceState) {
445        return null;
446    }
447
448    public View getView() {
449        return mView;
450    }
451
452    /**
453     * Called when the activity is ready for the fragment to run.  This is
454     * most useful for fragments that use {@link #setRetainInstance(boolean)}
455     * instance, as this tells the fragment when it is fully associated with
456     * the new activity instance.  This is called after {@link #onCreate(Bundle)}
457     * and before {@link #onStart()}.
458     *
459     * @param savedInstanceState If the fragment is being re-created from
460     * a previous saved state, this is the state.
461     */
462    public void onReady(Bundle savedInstanceState) {
463        mCalled = true;
464    }
465
466    /**
467     * Called when the Fragment is visible to the user.  This is generally
468     * tied to {@link Activity#onStart() Activity.onStart} of the containing
469     * Activity's lifecycle.
470     */
471    public void onStart() {
472        mCalled = true;
473    }
474
475    /**
476     * Called when the fragment is visible to the user and actively running.
477     * This is generally
478     * tied to {@link Activity#onResume() Activity.onResume} of the containing
479     * Activity's lifecycle.
480     */
481    public void onResume() {
482        mCalled = true;
483    }
484
485    public void onSaveInstanceState(Bundle outState) {
486    }
487
488    public void onConfigurationChanged(Configuration newConfig) {
489        mCalled = true;
490    }
491
492    /**
493     * Called when the Fragment is no longer resumed.  This is generally
494     * tied to {@link Activity#onPause() Activity.onPause} of the containing
495     * Activity's lifecycle.
496     */
497    public void onPause() {
498        mCalled = true;
499    }
500
501    /**
502     * Called when the Fragment is no longer started.  This is generally
503     * tied to {@link Activity#onStop() Activity.onStop} of the containing
504     * Activity's lifecycle.
505     */
506    public void onStop() {
507        mCalled = true;
508    }
509
510    public void onLowMemory() {
511        mCalled = true;
512    }
513
514    /**
515     * Called when the fragment is no longer in use.  This is called
516     * after {@link #onStop()} and before {@link #onDetach()}.
517     */
518    public void onDestroy() {
519        mCalled = true;
520    }
521
522    /**
523     * Called when the fragment is no longer attached to its activity.  This
524     * is called after {@link #onDestroy()}.
525     */
526    public void onDetach() {
527        mCalled = true;
528    }
529}
530