Fragment.java revision b31e84bc4513e46bac4be8f8d0513f78e360fb11
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.Menu;
29import android.view.MenuInflater;
30import android.view.MenuItem;
31import android.view.View;
32import android.view.ViewGroup;
33import android.view.animation.Animation;
34
35import java.lang.reflect.InvocationTargetException;
36import java.util.HashMap;
37
38final class FragmentState implements Parcelable {
39    static final String VIEW_STATE_TAG = "android:view_state";
40
41    final String mClassName;
42    final int mIndex;
43    final boolean mFromLayout;
44    final int mFragmentId;
45    final int mContainerId;
46    final String mTag;
47    final boolean mRetainInstance;
48
49    Bundle mSavedFragmentState;
50
51    Fragment mInstance;
52
53    public FragmentState(Fragment frag) {
54        mClassName = frag.getClass().getName();
55        mIndex = frag.mIndex;
56        mFromLayout = frag.mFromLayout;
57        mFragmentId = frag.mFragmentId;
58        mContainerId = frag.mContainerId;
59        mTag = frag.mTag;
60        mRetainInstance = frag.mRetainInstance;
61    }
62
63    public FragmentState(Parcel in) {
64        mClassName = in.readString();
65        mIndex = in.readInt();
66        mFromLayout = in.readInt() != 0;
67        mFragmentId = in.readInt();
68        mContainerId = in.readInt();
69        mTag = in.readString();
70        mRetainInstance = in.readInt() != 0;
71        mSavedFragmentState = in.readBundle();
72    }
73
74    public Fragment instantiate(Activity activity) {
75        if (mInstance != null) {
76            return mInstance;
77        }
78
79        try {
80            mInstance = Fragment.instantiate(activity, mClassName);
81        } catch (Exception e) {
82            throw new RuntimeException("Unable to restore fragment " + mClassName, e);
83        }
84
85        if (mSavedFragmentState != null) {
86            mSavedFragmentState.setClassLoader(activity.getClassLoader());
87            mInstance.mSavedFragmentState = mSavedFragmentState;
88            mInstance.mSavedViewState
89                    = mSavedFragmentState.getSparseParcelableArray(VIEW_STATE_TAG);
90        }
91        mInstance.setIndex(mIndex);
92        mInstance.mFromLayout = mFromLayout;
93        mInstance.mFragmentId = mFragmentId;
94        mInstance.mContainerId = mContainerId;
95        mInstance.mTag = mTag;
96        mInstance.mRetainInstance = mRetainInstance;
97
98        return mInstance;
99    }
100
101    public int describeContents() {
102        return 0;
103    }
104
105    public void writeToParcel(Parcel dest, int flags) {
106        dest.writeString(mClassName);
107        dest.writeInt(mIndex);
108        dest.writeInt(mFromLayout ? 1 : 0);
109        dest.writeInt(mFragmentId);
110        dest.writeInt(mContainerId);
111        dest.writeString(mTag);
112        dest.writeInt(mRetainInstance ? 1 : 0);
113        dest.writeBundle(mSavedFragmentState);
114    }
115
116    public static final Parcelable.Creator<FragmentState> CREATOR
117            = new Parcelable.Creator<FragmentState>() {
118        public FragmentState createFromParcel(Parcel in) {
119            return new FragmentState(in);
120        }
121
122        public FragmentState[] newArray(int size) {
123            return new FragmentState[size];
124        }
125    };
126}
127
128/**
129 * A Fragment is a piece of an application's user interface or behavior
130 * that can be placed in an {@link Activity}.
131 */
132public class Fragment implements ComponentCallbacks {
133    private static final HashMap<String, Class> sClassMap =
134            new HashMap<String, Class>();
135
136    static final int INITIALIZING = 0;  // Not yet created.
137    static final int CREATED = 1;       // Created.
138    static final int CONTENT = 2;       // View hierarchy content available.
139    static final int STARTED = 3;       // Created and started, not resumed.
140    static final int RESUMED = 4;       // Created started and resumed.
141
142    int mState = INITIALIZING;
143
144    // When instantiated from saved state, this is the saved state.
145    Bundle mSavedFragmentState;
146    SparseArray<Parcelable> mSavedViewState;
147
148    // Index into active fragment array.
149    int mIndex = -1;
150
151    // Internal unique name for this fragment;
152    String mWho;
153
154    // True if the fragment is in the list of added fragments.
155    boolean mAdded;
156
157    // Set to true if this fragment was instantiated from a layout file.
158    boolean mFromLayout;
159
160    // Number of active back stack entries this fragment is in.
161    int mBackStackNesting;
162
163    // Set as soon as a fragment is added to a transaction (or removed),
164    // to be able to do validation.
165    Activity mImmediateActivity;
166
167    // Activity this fragment is attached to.
168    Activity mActivity;
169
170    // The optional identifier for this fragment -- either the container ID if it
171    // was dynamically added to the view hierarchy, or the ID supplied in
172    // layout.
173    int mFragmentId;
174
175    // When a fragment is being dynamically added to the view hierarchy, this
176    // is the identifier of the parent container it is being added to.
177    int mContainerId;
178
179    // The optional named tag for this fragment -- usually used to find
180    // fragments that are not part of the layout.
181    String mTag;
182
183    // Set to true when the app has requested that this fragment be hidden
184    // from the user.
185    boolean mHidden;
186
187    // If set this fragment would like its instance retained across
188    // configuration changes.
189    boolean mRetainInstance;
190
191    // If set this fragment is being retained across the current config change.
192    boolean mRetaining;
193
194    // If set this fragment has menu items to contribute.
195    boolean mHasMenu;
196
197    // Used to verify that subclasses call through to super class.
198    boolean mCalled;
199
200    // If app has requested a specific animation, this is the one to use.
201    int mNextAnim;
202
203    // The parent container of the fragment after dynamically added to UI.
204    ViewGroup mContainer;
205
206    // The View generated for this fragment.
207    View mView;
208
209    public Fragment() {
210    }
211
212    static Fragment instantiate(Activity activity, String fname)
213            throws NoSuchMethodException, ClassNotFoundException,
214            IllegalArgumentException, InstantiationException,
215            IllegalAccessException, InvocationTargetException {
216        Class clazz = sClassMap.get(fname);
217
218        if (clazz == null) {
219            // Class not found in the cache, see if it's real, and try to add it
220            clazz = activity.getClassLoader().loadClass(fname);
221            sClassMap.put(fname, clazz);
222        }
223        return (Fragment)clazz.newInstance();
224    }
225
226    void restoreViewState() {
227        if (mSavedViewState != null) {
228            mView.restoreHierarchyState(mSavedViewState);
229            mSavedViewState = null;
230        }
231    }
232
233    void setIndex(int index) {
234        mIndex = index;
235        mWho = "android:fragment:" + mIndex;
236   }
237
238    void clearIndex() {
239        mIndex = -1;
240        mWho = null;
241    }
242
243    /**
244     * Subclasses can not override equals().
245     */
246    @Override final public boolean equals(Object o) {
247        return super.equals(o);
248    }
249
250    /**
251     * Subclasses can not override hashCode().
252     */
253    @Override final public int hashCode() {
254        return super.hashCode();
255    }
256
257    @Override
258    public String toString() {
259        StringBuilder sb = new StringBuilder(128);
260        sb.append("Fragment{");
261        sb.append(Integer.toHexString(System.identityHashCode(this)));
262        if (mIndex >= 0) {
263            sb.append(" #");
264            sb.append(mIndex);
265        }
266        if (mFragmentId != 0) {
267            sb.append(" id=0x");
268            sb.append(Integer.toHexString(mFragmentId));
269        }
270        if (mTag != null) {
271            sb.append(" ");
272            sb.append(mTag);
273        }
274        sb.append('}');
275        return sb.toString();
276    }
277
278    /**
279     * Return the identifier this fragment is known by.  This is either
280     * the android:id value supplied in a layout or the container view ID
281     * supplied when adding the fragment.
282     */
283    final public int getId() {
284        return mFragmentId;
285    }
286
287    /**
288     * Get the tag name of the fragment, if specified.
289     */
290    final public String getTag() {
291        return mTag;
292    }
293
294    /**
295     * Return the Activity this fragment is currently associated with.
296     */
297    final public Activity getActivity() {
298        return mActivity;
299    }
300
301    /**
302     * Return true if the fragment is currently added to its activity.
303     */
304    final public boolean isAdded() {
305        return mActivity != null && mActivity.mFragments.mAdded.contains(this);
306    }
307
308    /**
309     * Return true if the fragment is currently visible to the user.  This means
310     * it: (1) has been added, (2) has its view attached to the window, and
311     * (3) is not hidden.
312     */
313    final public boolean isVisible() {
314        return isAdded() && !isHidden() && mView != null
315                && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
316    }
317
318    /**
319     * Return true if the fragment has been hidden.  By default fragments
320     * are shown.  You can find out about changes to this state with
321     * {@link #onHiddenChanged}.  Note that the hidden state is orthogonal
322     * to other states -- that is, to be visible to the user, a fragment
323     * must be both started and not hidden.
324     */
325    final public boolean isHidden() {
326        return mHidden;
327    }
328
329    /**
330     * Called when the hidden state (as returned by {@link #isHidden()} of
331     * the fragment has changed.  Fragments start out not hidden; this will
332     * be called whenever the fragment changes state from that.
333     * @param hidden True if the fragment is now hidden, false if it is not
334     * visible.
335     */
336    public void onHiddenChanged(boolean hidden) {
337    }
338
339    /**
340     * Control whether a fragment instance is retained across Activity
341     * re-creation (such as from a configuration change).  This can only
342     * be used with fragments not in the back stack.  If set, the fragment
343     * lifecycle will be slightly different when an activity is recreated:
344     * <ul>
345     * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
346     * will be, because the fragment is being detached from its current activity).
347     * <li> {@link #onCreate(Bundle)} will not be called since the fragment
348     * is not being re-created.
349     * <li> {@link #onAttach(Activity)} and {@link #onReady(Bundle)} <b>will</b>
350     * still be called.
351     * </ul>
352     */
353    public void setRetainInstance(boolean retain) {
354        mRetainInstance = retain;
355    }
356
357    final public boolean getRetainInstance() {
358        return mRetainInstance;
359    }
360
361    /**
362     * Report that this fragment would like to participate in populating
363     * the options menu by receiving a call to {@link #onCreateOptionsMenu(Menu)}
364     * and related methods.
365     *
366     * @param hasMenu If true, the fragment has menu items to contribute.
367     */
368    public void setHasOptionsMenu(boolean hasMenu) {
369        if (mHasMenu != hasMenu) {
370            mHasMenu = hasMenu;
371            if (isAdded() && !isHidden()) {
372                mActivity.invalidateOptionsMenu();
373            }
374        }
375    }
376
377    /**
378     * Call {@link Activity#startActivity(Intent)} on the fragment's
379     * containing Activity.
380     */
381    public void startActivity(Intent intent) {
382        mActivity.startActivityFromFragment(this, intent, -1);
383    }
384
385    /**
386     * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's
387     * containing Activity.
388     */
389    public void startActivityForResult(Intent intent, int requestCode) {
390        mActivity.startActivityFromFragment(this, intent, requestCode);
391    }
392
393    /**
394     * Receive the result from a previous call to
395     * {@link #startActivityForResult(Intent, int)}.  This follows the
396     * related Activity API as described there in
397     * {@link Activity#onActivityResult(int, int, Intent)}.
398     *
399     * @param requestCode The integer request code originally supplied to
400     *                    startActivityForResult(), allowing you to identify who this
401     *                    result came from.
402     * @param resultCode The integer result code returned by the child activity
403     *                   through its setResult().
404     * @param data An Intent, which can return result data to the caller
405     *               (various data can be attached to Intent "extras").
406     */
407    public void onActivityResult(int requestCode, int resultCode, Intent data) {
408    }
409
410    /**
411     * Called when a fragment is being created as part of a view layout
412     * inflation, typically from setting the content view of an activity.  This
413     * will be called both the first time the fragment is created, as well
414     * later when it is being re-created from its saved state (which is also
415     * given here).
416     *
417     * XXX This is kind-of yucky...  maybe we could just supply the
418     * AttributeSet to onCreate()?
419     *
420     * @param activity The Activity that is inflating the fragment.
421     * @param attrs The attributes at the tag where the fragment is
422     * being created.
423     * @param savedInstanceState If the fragment is being re-created from
424     * a previous saved state, this is the state.
425     */
426    public void onInflate(Activity activity, AttributeSet attrs,
427            Bundle savedInstanceState) {
428        mCalled = true;
429    }
430
431    /**
432     * Called when a fragment is first attached to its activity.
433     * {@link #onCreate(Bundle)} will be called after this.
434     */
435    public void onAttach(Activity activity) {
436        mCalled = true;
437    }
438
439    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
440        return null;
441    }
442
443    /**
444     * Called to do initial creation of a fragment.  This is called after
445     * {@link #onAttach(Activity)} and before {@link #onReady(Bundle)}.
446     * @param savedInstanceState If the fragment is being re-created from
447     * a previous saved state, this is the state.
448     */
449    public void onCreate(Bundle savedInstanceState) {
450        mCalled = true;
451    }
452
453    /**
454     * Called to have the fragment instantiate its user interface view.
455     * This is optional, and non-graphical fragments can return null (which
456     * is the default implementation).  This will be called between
457     * {@link #onCreate(Bundle)} and {@link #onReady(Bundle)}.
458     *
459     * @param inflater The LayoutInflater object that can be used to inflate
460     * any views in the fragment,
461     * @param container If non-null, this is the parent view that the fragment's
462     * UI should be attached to.  The fragment should not add the view itself,
463     * but this can be used to generate the LayoutParams of the view.
464     * @param savedInstanceState If non-null, this fragment is being re-constructed
465     * from a previous saved state as given here.
466     *
467     * @return Return the View for the fragment's UI, or null.
468     */
469    public View onCreateView(LayoutInflater inflater, ViewGroup container,
470            Bundle savedInstanceState) {
471        return null;
472    }
473
474    public View getView() {
475        return mView;
476    }
477
478    /**
479     * Called when the activity is ready for the fragment to run.  This is
480     * most useful for fragments that use {@link #setRetainInstance(boolean)}
481     * instance, as this tells the fragment when it is fully associated with
482     * the new activity instance.  This is called after {@link #onCreate(Bundle)}
483     * and before {@link #onStart()}.
484     *
485     * @param savedInstanceState If the fragment is being re-created from
486     * a previous saved state, this is the state.
487     */
488    public void onReady(Bundle savedInstanceState) {
489        mCalled = true;
490    }
491
492    /**
493     * Called when the Fragment is visible to the user.  This is generally
494     * tied to {@link Activity#onStart() Activity.onStart} of the containing
495     * Activity's lifecycle.
496     */
497    public void onStart() {
498        mCalled = true;
499    }
500
501    /**
502     * Called when the fragment is visible to the user and actively running.
503     * This is generally
504     * tied to {@link Activity#onResume() Activity.onResume} of the containing
505     * Activity's lifecycle.
506     */
507    public void onResume() {
508        mCalled = true;
509    }
510
511    public void onSaveInstanceState(Bundle outState) {
512    }
513
514    public void onConfigurationChanged(Configuration newConfig) {
515        mCalled = true;
516    }
517
518    /**
519     * Called when the Fragment is no longer resumed.  This is generally
520     * tied to {@link Activity#onPause() Activity.onPause} of the containing
521     * Activity's lifecycle.
522     */
523    public void onPause() {
524        mCalled = true;
525    }
526
527    /**
528     * Called when the Fragment is no longer started.  This is generally
529     * tied to {@link Activity#onStop() Activity.onStop} of the containing
530     * Activity's lifecycle.
531     */
532    public void onStop() {
533        mCalled = true;
534    }
535
536    public void onLowMemory() {
537        mCalled = true;
538    }
539
540    /**
541     * Called when the fragment is no longer in use.  This is called
542     * after {@link #onStop()} and before {@link #onDetach()}.
543     */
544    public void onDestroy() {
545        mCalled = true;
546    }
547
548    /**
549     * Called when the fragment is no longer attached to its activity.  This
550     * is called after {@link #onDestroy()}.
551     */
552    public void onDetach() {
553        mCalled = true;
554    }
555
556    /**
557     * Initialize the contents of the Activity's standard options menu.  You
558     * should place your menu items in to <var>menu</var>.  For this method
559     * to be called, you must have first called {@link #setHasMenu}.  See
560     * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu}
561     * for more information.
562     *
563     * @param menu The options menu in which you place your items.
564     *
565     * @see #setHasMenu
566     * @see #onPrepareOptionsMenu
567     * @see #onOptionsItemSelected
568     */
569    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
570    }
571
572    /**
573     * Prepare the Screen's standard options menu to be displayed.  This is
574     * called right before the menu is shown, every time it is shown.  You can
575     * use this method to efficiently enable/disable items or otherwise
576     * dynamically modify the contents.  See
577     * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu}
578     * for more information.
579     *
580     * @param menu The options menu as last shown or first initialized by
581     *             onCreateOptionsMenu().
582     *
583     * @see #setHasMenu
584     * @see #onCreateOptionsMenu
585     */
586    public void onPrepareOptionsMenu(Menu menu) {
587    }
588
589    /**
590     * This hook is called whenever an item in your options menu is selected.
591     * The default implementation simply returns false to have the normal
592     * processing happen (calling the item's Runnable or sending a message to
593     * its Handler as appropriate).  You can use this method for any items
594     * for which you would like to do processing without those other
595     * facilities.
596     *
597     * <p>Derived classes should call through to the base class for it to
598     * perform the default menu handling.
599     *
600     * @param item The menu item that was selected.
601     *
602     * @return boolean Return false to allow normal menu processing to
603     *         proceed, true to consume it here.
604     *
605     * @see #onCreateOptionsMenu
606     */
607    public boolean onOptionsItemSelected(MenuItem item) {
608        return false;
609    }
610
611    /**
612     * This hook is called whenever the options menu is being closed (either by the user canceling
613     * the menu with the back/menu button, or when an item is selected).
614     *
615     * @param menu The options menu as last shown or first initialized by
616     *             onCreateOptionsMenu().
617     */
618    public void onOptionsMenuClosed(Menu menu) {
619    }
620}
621