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);
1465424d16916b2fb65ff009051e4f9d02584d07b53Piet Delport    }
1470adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1485424d16916b2fb65ff009051e4f9d02584d07b53Piet Delport    private void ensureHierarchy(Context context) {
1490adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        // If owner hasn't made its own view hierarchy, then as a convenience
1500adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        // we will construct a standard one here.
1510adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        if (findViewById(android.R.id.tabs) == null) {
1520adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            LinearLayout ll = new LinearLayout(context);
1530adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            ll.setOrientation(LinearLayout.VERTICAL);
1540adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            addView(ll, new FrameLayout.LayoutParams(
1550adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ViewGroup.LayoutParams.FILL_PARENT,
1560adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ViewGroup.LayoutParams.FILL_PARENT));
1570adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1580adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            TabWidget tw = new TabWidget(context);
1590adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            tw.setId(android.R.id.tabs);
1600adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            tw.setOrientation(TabWidget.HORIZONTAL);
1610adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            ll.addView(tw, new LinearLayout.LayoutParams(
1620adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ViewGroup.LayoutParams.FILL_PARENT,
1630adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ViewGroup.LayoutParams.WRAP_CONTENT, 0));
1640adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1650adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            FrameLayout fl = new FrameLayout(context);
1660adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            fl.setId(android.R.id.tabcontent);
1670adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));
1680adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1690adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mRealTabContent = fl = new FrameLayout(context);
1700adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mRealTabContent.setId(mContainerId);
1710adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            ll.addView(fl, new LinearLayout.LayoutParams(
1720adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    LinearLayout.LayoutParams.FILL_PARENT, 0, 1));
1730adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
1740adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
1750adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1760adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    /**
1770adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn     * @deprecated Don't call the original TabHost setup, you must instead
1780adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn     * call {@link #setup(Context, FragmentManager)} or
1790adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn     * {@link #setup(Context, FragmentManager, int)}.
1800adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn     */
1810adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    @Override @Deprecated
1820adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void setup() {
1830adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        throw new IllegalStateException(
1840adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                "Must call setup() that takes a Context and FragmentManager");
1850adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
1860adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1870adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void setup(Context context, FragmentManager manager) {
1885424d16916b2fb65ff009051e4f9d02584d07b53Piet Delport        ensureHierarchy(context);  // Ensure views required by super.setup()
1890adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        super.setup();
1900adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mContext = context;
1910adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mFragmentManager = manager;
1920adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        ensureContent();
1930adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
1940adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
1950adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void setup(Context context, FragmentManager manager, int containerId) {
1965424d16916b2fb65ff009051e4f9d02584d07b53Piet Delport        ensureHierarchy(context);  // Ensure views required by super.setup()
1970adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        super.setup();
1980adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mContext = context;
1990adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mFragmentManager = manager;
2000adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mContainerId = containerId;
2010adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        ensureContent();
2020adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mRealTabContent.setId(containerId);
20374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
20474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // We must have an ID to be able to save/restore our state.  If
20574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // the owner hasn't set one at this point, we will set it ourself.
20674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (getId() == View.NO_ID) {
20774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            setId(android.R.id.tabhost);
20874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
2090adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2100adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2110adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    private void ensureContent() {
2120adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        if (mRealTabContent == null) {
2130adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mRealTabContent = (FrameLayout)findViewById(mContainerId);
2140adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            if (mRealTabContent == null) {
2150adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                throw new IllegalStateException(
2160adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                        "No tab content FrameLayout found for id " + mContainerId);
2170adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            }
2180adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
2190adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2200adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2210adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    @Override
2220adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void setOnTabChangedListener(OnTabChangeListener l) {
2230adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        mOnTabChangeListener = l;
2240adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2250adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2260adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
2270adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        tabSpec.setContent(new DummyTabFactory(mContext));
2280adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        String tag = tabSpec.getTag();
2290adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2300adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        TabInfo info = new TabInfo(tag, clss, args);
2310adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
23274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (mAttached) {
23374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            // If we are already attached to the window, then check to make
23474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            // sure this tab's fragment is inactive if it exists.  This shouldn't
23574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            // normally happen.
23674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            info.fragment = mFragmentManager.findFragmentByTag(tag);
23774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (info.fragment != null && !info.fragment.isDetached()) {
23874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                FragmentTransaction ft = mFragmentManager.beginTransaction();
23974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                ft.detach(info.fragment);
24074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                ft.commit();
24174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
2420adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
2430adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
24474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        mTabs.add(info);
2450adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        addTab(tabSpec);
2460adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
2470adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
2480adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    @Override
24974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    protected void onAttachedToWindow() {
25074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        super.onAttachedToWindow();
25174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
25274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        String currentTab = getCurrentTabTag();
25374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
25474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // Go through all tabs and make sure their fragments match
25574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // the correct state.
25674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        FragmentTransaction ft = null;
25774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        for (int i=0; i<mTabs.size(); i++) {
25874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            TabInfo tab = mTabs.get(i);
25974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);
26074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (tab.fragment != null && !tab.fragment.isDetached()) {
26174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                if (tab.tag.equals(currentTab)) {
26274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // The fragment for this tab is already there and
26374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // active, and it is what we really want to have
26474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // as the current tab.  Nothing to do.
26574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    mLastTab = tab;
26674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                } else {
26774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // This fragment was restored in the active state,
26874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    // but is not the current tab.  Deactivate it.
26974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    if (ft == null) {
27074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                        ft = mFragmentManager.beginTransaction();
27174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    }
27274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                    ft.detach(tab.fragment);
27374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                }
27474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
27574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
27674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
27774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // We are now ready to go.  Make sure we are switched to the
27874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        // correct tab.
27974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        mAttached = true;
28074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        ft = doTabChanged(currentTab, ft);
28174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (ft != null) {
28274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            ft.commit();
28374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            mFragmentManager.executePendingTransactions();
28474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
28574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
28674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
28774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    @Override
28874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    protected void onDetachedFromWindow() {
28974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        super.onDetachedFromWindow();
29074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        mAttached = false;
29174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
29274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
29374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    @Override
29474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    protected Parcelable onSaveInstanceState() {
29574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        Parcelable superState = super.onSaveInstanceState();
29674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        SavedState ss = new SavedState(superState);
29774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        ss.curTab = getCurrentTabTag();
29874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        return ss;
29974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
30074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
30174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    @Override
30274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    protected void onRestoreInstanceState(Parcelable state) {
30374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        SavedState ss = (SavedState)state;
30474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        super.onRestoreInstanceState(ss.getSuperState());
30574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        setCurrentTabByTag(ss.curTab);
30674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
30774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
30874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    @Override
3090adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    public void onTabChanged(String tabId) {
31074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (mAttached) {
31174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            FragmentTransaction ft = doTabChanged(tabId, null);
31274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (ft != null) {
31374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                ft.commit();
31474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
31574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
31674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (mOnTabChangeListener != null) {
31774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            mOnTabChangeListener.onTabChanged(tabId);
31874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
31974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    }
32074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn
32174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn    private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
32274c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        TabInfo newTab = null;
32374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        for (int i=0; i<mTabs.size(); i++) {
32474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            TabInfo tab = mTabs.get(i);
32574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (tab.tag.equals(tabId)) {
32674c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                newTab = tab;
32774c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
32874c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
32974c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        if (newTab == null) {
33074c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            throw new IllegalStateException("No tab known for tag " + tabId);
33174c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        }
3320adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        if (mLastTab != newTab) {
33374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            if (ft == null) {
33474c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn                ft = mFragmentManager.beginTransaction();
33574c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn            }
3360adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            if (mLastTab != null) {
3370adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                if (mLastTab.fragment != null) {
3380adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ft.detach(mLastTab.fragment);
3390adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                }
3400adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            }
3410adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            if (newTab != null) {
3420adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                if (newTab.fragment == null) {
3430adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    newTab.fragment = Fragment.instantiate(mContext,
3440adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                            newTab.clss.getName(), newTab.args);
3450adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ft.add(mContainerId, newTab.fragment, newTab.tag);
3460adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                } else {
3470adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                    ft.attach(newTab.fragment);
3480adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn                }
3490adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            }
3500adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn
3510adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn            mLastTab = newTab;
3520adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn        }
35374c671b3b67000bf16b4865a8d361344310dccbeDianne Hackborn        return ft;
3540adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn    }
3550adacc1aa313d757ae1c517152cef838e0f35c13Dianne Hackborn}
356