10adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn/*
20adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * Copyright (C) 2012 The Android Open Source Project
30adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn *
40adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License");
50adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * you may not use this file except in compliance with the License.
60adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * You may obtain a copy of the License at
70adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn *
80adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn *      http://www.apache.org/licenses/LICENSE-2.0
90adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn *
100adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * Unless required by applicable law or agreed to in writing, software
110adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS,
120adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * See the License for the specific language governing permissions and
140adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * limitations under the License.
150adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn */
160adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
170adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornpackage android.support.v4.app;
180adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackbornimport java.util.ArrayList;
200adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
210adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.content.Context;
220adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.content.res.TypedArray;
230adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.os.Bundle;
2474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackbornimport android.os.Parcel;
2574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackbornimport android.os.Parcelable;
260adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.util.AttributeSet;
270adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.view.View;
280adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.view.ViewGroup;
290adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.widget.FrameLayout;
300adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.widget.LinearLayout;
310adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.widget.TabHost;
320adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornimport android.widget.TabWidget;
330adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
340adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn/**
350adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * Special TabHost that allows the use of {@link Fragment} objects for
360adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * its tab content.  When placing this in a view hierarchy, after inflating
370adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * the hierarchy you must call {@link #setup(Context, FragmentManager, int)}
380adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn * to complete the initialization of the tab host.
3974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn *
4074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn * <p>Here is a simple example of using a FragmentTabHost in an Activity:
4174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn *
4274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn * {@sample development/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java
4374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn *      complete}
4474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn *
4574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn * <p>This can also be used inside of a fragment through fragment nesting:
4674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn *
4774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn * {@sample development/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java
4874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn *      complete}
490adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn */
500adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackbornpublic class FragmentTabHost extends TabHost
510adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        implements TabHost.OnTabChangeListener {
5274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
530adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private FrameLayout mRealTabContent;
540adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private Context mContext;
550adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private FragmentManager mFragmentManager;
560adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private int mContainerId;
570adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private TabHost.OnTabChangeListener mOnTabChangeListener;
580adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private TabInfo mLastTab;
5974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    private boolean mAttached;
600adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
610adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    static final class TabInfo {
620adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        private final String tag;
630adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        private final Class<?> clss;
640adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        private final Bundle args;
650adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        private Fragment fragment;
660adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
670adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        TabInfo(String _tag, Class<?> _class, Bundle _args) {
680adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            tag = _tag;
690adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            clss = _class;
700adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            args = _args;
710adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
720adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
730adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
740adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    static class DummyTabFactory implements TabHost.TabContentFactory {
750adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        private final Context mContext;
760adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
770adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        public DummyTabFactory(Context context) {
780adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mContext = context;
790adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
800adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
810adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        @Override
820adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        public View createTabContent(String tag) {
830adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            View v = new View(mContext);
840adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            v.setMinimumWidth(0);
850adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            v.setMinimumHeight(0);
860adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            return v;
870adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
880adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
890adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
9074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    static class SavedState extends BaseSavedState {
9174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        String curTab;
9274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
9374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        SavedState(Parcelable superState) {
9474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            super(superState);
9574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
9674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
9774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        private SavedState(Parcel in) {
9874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            super(in);
9974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            curTab = in.readString();
10074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
10174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
10274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        @Override
10374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        public void writeToParcel(Parcel out, int flags) {
10474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            super.writeToParcel(out, flags);
10574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            out.writeString(curTab);
10674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
10774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
10874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        @Override
10974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        public String toString() {
11074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            return "FragmentTabHost.SavedState{"
11174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    + Integer.toHexString(System.identityHashCode(this))
11274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    + " curTab=" + curTab + "}";
11374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
11474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
11574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        public static final Parcelable.Creator<SavedState> CREATOR
11674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                = new Parcelable.Creator<SavedState>() {
11774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            public SavedState createFromParcel(Parcel in) {
11874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                return new SavedState(in);
11974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
12074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
12174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            public SavedState[] newArray(int size) {
12274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                return new SavedState[size];
12374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
12474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        };
12574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
12674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
1270adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public FragmentTabHost(Context context) {
1280adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        // Note that we call through to the version that takes an AttributeSet,
1290adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        // because the simple Context construct can result in a broken object!
1300adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        super(context, null);
1310adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        initFragmentTabHost(context, null);
1320adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
1330adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1340adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public FragmentTabHost(Context context, AttributeSet attrs) {
1350adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        super(context, attrs);
1360adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        initFragmentTabHost(context, attrs);
1370adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
1380adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1390adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private void initFragmentTabHost(Context context, AttributeSet attrs) {
1400adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        TypedArray a = context.obtainStyledAttributes(attrs,
1410adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                new int[] { android.R.attr.inflatedId }, 0, 0);
1420adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mContainerId = a.getResourceId(0, 0);
1430adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        a.recycle();
1440adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1450adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        super.setOnTabChangedListener(this);
1460adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1470adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        // If owner hasn't made its own view hierarchy, then as a convenience
1480adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        // we will construct a standard one here.
1490adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        if (findViewById(android.R.id.tabs) == null) {
1500adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            LinearLayout ll = new LinearLayout(context);
1510adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            ll.setOrientation(LinearLayout.VERTICAL);
1520adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            addView(ll, new FrameLayout.LayoutParams(
1530adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ViewGroup.LayoutParams.FILL_PARENT,
1540adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ViewGroup.LayoutParams.FILL_PARENT));
1550adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1560adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            TabWidget tw = new TabWidget(context);
1570adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            tw.setId(android.R.id.tabs);
1580adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            tw.setOrientation(TabWidget.HORIZONTAL);
1590adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            ll.addView(tw, new LinearLayout.LayoutParams(
1600adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ViewGroup.LayoutParams.FILL_PARENT,
1610adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ViewGroup.LayoutParams.WRAP_CONTENT, 0));
1620adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1630adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            FrameLayout fl = new FrameLayout(context);
1640adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            fl.setId(android.R.id.tabcontent);
1650adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));
1660adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1670adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mRealTabContent = fl = new FrameLayout(context);
1680adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mRealTabContent.setId(mContainerId);
1690adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            ll.addView(fl, new LinearLayout.LayoutParams(
1700adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    LinearLayout.LayoutParams.FILL_PARENT, 0, 1));
1710adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
1720adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
1730adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1740adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    /**
1750adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn     * @deprecated Don't call the original TabHost setup, you must instead
1760adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn     * call {@link #setup(Context, FragmentManager)} or
1770adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn     * {@link #setup(Context, FragmentManager, int)}.
1780adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn     */
1790adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    @Override @Deprecated
1800adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void setup() {
1810adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        throw new IllegalStateException(
1820adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                "Must call setup() that takes a Context and FragmentManager");
1830adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
1840adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1850adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void setup(Context context, FragmentManager manager) {
1860adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        super.setup();
1870adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mContext = context;
1880adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mFragmentManager = manager;
1890adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        ensureContent();
1900adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
1910adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1920adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void setup(Context context, FragmentManager manager, int containerId) {
1930adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        super.setup();
1940adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mContext = context;
1950adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mFragmentManager = manager;
1960adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mContainerId = containerId;
1970adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        ensureContent();
1980adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mRealTabContent.setId(containerId);
19974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
20074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // We must have an ID to be able to save/restore our state.  If
20174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // the owner hasn't set one at this point, we will set it ourself.
20274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (getId() == View.NO_ID) {
20374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            setId(android.R.id.tabhost);
20474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
2050adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2060adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2070adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private void ensureContent() {
2080adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        if (mRealTabContent == null) {
2090adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mRealTabContent = (FrameLayout)findViewById(mContainerId);
2100adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            if (mRealTabContent == null) {
2110adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                throw new IllegalStateException(
2120adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                        "No tab content FrameLayout found for id " + mContainerId);
2130adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            }
2140adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
2150adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2160adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2170adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    @Override
2180adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void setOnTabChangedListener(OnTabChangeListener l) {
2190adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mOnTabChangeListener = l;
2200adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2210adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2220adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
2230adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        tabSpec.setContent(new DummyTabFactory(mContext));
2240adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        String tag = tabSpec.getTag();
2250adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2260adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        TabInfo info = new TabInfo(tag, clss, args);
2270adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
22874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (mAttached) {
22974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            // If we are already attached to the window, then check to make
23074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            // sure this tab's fragment is inactive if it exists.  This shouldn't
23174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            // normally happen.
23274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            info.fragment = mFragmentManager.findFragmentByTag(tag);
23374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (info.fragment != null && !info.fragment.isDetached()) {
23474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                FragmentTransaction ft = mFragmentManager.beginTransaction();
23574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                ft.detach(info.fragment);
23674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                ft.commit();
23774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
2380adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
2390adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
24074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        mTabs.add(info);
2410adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        addTab(tabSpec);
2420adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2430adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2440adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    @Override
24574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    protected void onAttachedToWindow() {
24674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        super.onAttachedToWindow();
24774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
24874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        String currentTab = getCurrentTabTag();
24974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
25074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // Go through all tabs and make sure their fragments match
25174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // the correct state.
25274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        FragmentTransaction ft = null;
25374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        for (int i=0; i<mTabs.size(); i++) {
25474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            TabInfo tab = mTabs.get(i);
25574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);
25674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (tab.fragment != null && !tab.fragment.isDetached()) {
25774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                if (tab.tag.equals(currentTab)) {
25874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // The fragment for this tab is already there and
25974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // active, and it is what we really want to have
26074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // as the current tab.  Nothing to do.
26174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    mLastTab = tab;
26274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                } else {
26374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // This fragment was restored in the active state,
26474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // but is not the current tab.  Deactivate it.
26574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    if (ft == null) {
26674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                        ft = mFragmentManager.beginTransaction();
26774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    }
26874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    ft.detach(tab.fragment);
26974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                }
27074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
27174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
27274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
27374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // We are now ready to go.  Make sure we are switched to the
27474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // correct tab.
27574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        mAttached = true;
27674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        ft = doTabChanged(currentTab, ft);
27774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (ft != null) {
27874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            ft.commit();
27974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            mFragmentManager.executePendingTransactions();
28074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
28174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
28274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
28374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    @Override
28474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    protected void onDetachedFromWindow() {
28574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        super.onDetachedFromWindow();
28674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        mAttached = false;
28774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
28874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
28974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    @Override
29074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    protected Parcelable onSaveInstanceState() {
29174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        Parcelable superState = super.onSaveInstanceState();
29274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        SavedState ss = new SavedState(superState);
29374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        ss.curTab = getCurrentTabTag();
29474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        return ss;
29574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
29674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
29774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    @Override
29874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    protected void onRestoreInstanceState(Parcelable state) {
29974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        SavedState ss = (SavedState)state;
30074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        super.onRestoreInstanceState(ss.getSuperState());
30174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        setCurrentTabByTag(ss.curTab);
30274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
30374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
30474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    @Override
3050adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void onTabChanged(String tabId) {
30674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (mAttached) {
30774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            FragmentTransaction ft = doTabChanged(tabId, null);
30874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (ft != null) {
30974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                ft.commit();
31074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
31174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
31274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (mOnTabChangeListener != null) {
31374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            mOnTabChangeListener.onTabChanged(tabId);
31474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
31574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
31674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
31774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
31874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        TabInfo newTab = null;
31974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        for (int i=0; i<mTabs.size(); i++) {
32074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            TabInfo tab = mTabs.get(i);
32174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (tab.tag.equals(tabId)) {
32274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                newTab = tab;
32374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
32474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
32574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (newTab == null) {
32674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            throw new IllegalStateException("No tab known for tag " + tabId);
32774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
3280adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        if (mLastTab != newTab) {
32974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (ft == null) {
33074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                ft = mFragmentManager.beginTransaction();
33174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
3320adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            if (mLastTab != null) {
3330adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                if (mLastTab.fragment != null) {
3340adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ft.detach(mLastTab.fragment);
3350adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                }
3360adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            }
3370adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            if (newTab != null) {
3380adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                if (newTab.fragment == null) {
3390adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    newTab.fragment = Fragment.instantiate(mContext,
3400adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                            newTab.clss.getName(), newTab.args);
3410adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ft.add(mContainerId, newTab.fragment, newTab.tag);
3420adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                } else {
3430adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ft.attach(newTab.fragment);
3440adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                }
3450adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            }
3460adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
3470adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mLastTab = newTab;
3480adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
34974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        return ft;
3500adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
3510adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn}
352