Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2006 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 
     17 package android.widget;
     18 
     19 import com.android.internal.R;
     20 
     21 import android.app.LocalActivityManager;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.res.TypedArray;
     25 import android.graphics.drawable.Drawable;
     26 import android.os.Build;
     27 import android.text.TextUtils;
     28 import android.util.AttributeSet;
     29 import android.view.KeyEvent;
     30 import android.view.LayoutInflater;
     31 import android.view.SoundEffectConstants;
     32 import android.view.View;
     33 import android.view.ViewGroup;
     34 import android.view.ViewTreeObserver;
     35 import android.view.Window;
     36 import android.view.accessibility.AccessibilityEvent;
     37 import android.view.accessibility.AccessibilityNodeInfo;
     38 
     39 import java.util.ArrayList;
     40 import java.util.List;
     41 
     42 /**
     43  * Container for a tabbed window view. This object holds two children: a set of tab labels that the
     44  * user clicks to select a specific tab, and a FrameLayout object that displays the contents of that
     45  * page. The individual elements are typically controlled using this container object, rather than
     46  * setting values on the child elements themselves.
     47  *
     48  */
     49 public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
     50 
     51     private static final int TABWIDGET_LOCATION_LEFT = 0;
     52     private static final int TABWIDGET_LOCATION_TOP = 1;
     53     private static final int TABWIDGET_LOCATION_RIGHT = 2;
     54     private static final int TABWIDGET_LOCATION_BOTTOM = 3;
     55     private TabWidget mTabWidget;
     56     private FrameLayout mTabContent;
     57     private List<TabSpec> mTabSpecs = new ArrayList<TabSpec>(2);
     58     /**
     59      * This field should be made private, so it is hidden from the SDK.
     60      * {@hide}
     61      */
     62     protected int mCurrentTab = -1;
     63     private View mCurrentView = null;
     64     /**
     65      * This field should be made private, so it is hidden from the SDK.
     66      * {@hide}
     67      */
     68     protected LocalActivityManager mLocalActivityManager = null;
     69     private OnTabChangeListener mOnTabChangeListener;
     70     private OnKeyListener mTabKeyListener;
     71 
     72     private int mTabLayoutId;
     73 
     74     public TabHost(Context context) {
     75         super(context);
     76         initTabHost();
     77     }
     78 
     79     public TabHost(Context context, AttributeSet attrs) {
     80         this(context, attrs, com.android.internal.R.attr.tabWidgetStyle);
     81     }
     82 
     83     public TabHost(Context context, AttributeSet attrs, int defStyleAttr) {
     84         this(context, attrs, defStyleAttr, 0);
     85     }
     86 
     87     public TabHost(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     88         super(context, attrs);
     89 
     90         final TypedArray a = context.obtainStyledAttributes(
     91                 attrs, com.android.internal.R.styleable.TabWidget, defStyleAttr, defStyleRes);
     92 
     93         mTabLayoutId = a.getResourceId(R.styleable.TabWidget_tabLayout, 0);
     94         a.recycle();
     95 
     96         if (mTabLayoutId == 0) {
     97             // In case the tabWidgetStyle does not inherit from Widget.TabWidget and tabLayout is
     98             // not defined.
     99             mTabLayoutId = R.layout.tab_indicator_holo;
    100         }
    101 
    102         initTabHost();
    103     }
    104 
    105     private void initTabHost() {
    106         setFocusableInTouchMode(true);
    107         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
    108 
    109         mCurrentTab = -1;
    110         mCurrentView = null;
    111     }
    112 
    113     /**
    114      * Get a new {@link TabSpec} associated with this tab host.
    115      * @param tag required tag of tab.
    116      */
    117     public TabSpec newTabSpec(String tag) {
    118         return new TabSpec(tag);
    119     }
    120 
    121 
    122 
    123     /**
    124       * <p>Call setup() before adding tabs if loading TabHost using findViewById().
    125       * <i><b>However</i></b>: You do not need to call setup() after getTabHost()
    126       * in {@link android.app.TabActivity TabActivity}.
    127       * Example:</p>
    128 <pre>mTabHost = (TabHost)findViewById(R.id.tabhost);
    129 mTabHost.setup();
    130 mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
    131       */
    132     public void setup() {
    133         mTabWidget = (TabWidget) findViewById(com.android.internal.R.id.tabs);
    134         if (mTabWidget == null) {
    135             throw new RuntimeException(
    136                     "Your TabHost must have a TabWidget whose id attribute is 'android.R.id.tabs'");
    137         }
    138 
    139         // KeyListener to attach to all tabs. Detects non-navigation keys
    140         // and relays them to the tab content.
    141         mTabKeyListener = new OnKeyListener() {
    142             public boolean onKey(View v, int keyCode, KeyEvent event) {
    143                 switch (keyCode) {
    144                     case KeyEvent.KEYCODE_DPAD_CENTER:
    145                     case KeyEvent.KEYCODE_DPAD_LEFT:
    146                     case KeyEvent.KEYCODE_DPAD_RIGHT:
    147                     case KeyEvent.KEYCODE_DPAD_UP:
    148                     case KeyEvent.KEYCODE_DPAD_DOWN:
    149                     case KeyEvent.KEYCODE_ENTER:
    150                         return false;
    151 
    152                 }
    153                 mTabContent.requestFocus(View.FOCUS_FORWARD);
    154                 return mTabContent.dispatchKeyEvent(event);
    155             }
    156 
    157         };
    158 
    159         mTabWidget.setTabSelectionListener(new TabWidget.OnTabSelectionChanged() {
    160             public void onTabSelectionChanged(int tabIndex, boolean clicked) {
    161                 setCurrentTab(tabIndex);
    162                 if (clicked) {
    163                     mTabContent.requestFocus(View.FOCUS_FORWARD);
    164                 }
    165             }
    166         });
    167 
    168         mTabContent = (FrameLayout) findViewById(com.android.internal.R.id.tabcontent);
    169         if (mTabContent == null) {
    170             throw new RuntimeException(
    171                     "Your TabHost must have a FrameLayout whose id attribute is "
    172                             + "'android.R.id.tabcontent'");
    173         }
    174     }
    175 
    176     @Override
    177     public void sendAccessibilityEvent(int eventType) {
    178         /* avoid super class behavior - TabWidget sends the right events */
    179     }
    180 
    181     /**
    182      * If you are using {@link TabSpec#setContent(android.content.Intent)}, this
    183      * must be called since the activityGroup is needed to launch the local activity.
    184      *
    185      * This is done for you if you extend {@link android.app.TabActivity}.
    186      * @param activityGroup Used to launch activities for tab content.
    187      */
    188     public void setup(LocalActivityManager activityGroup) {
    189         setup();
    190         mLocalActivityManager = activityGroup;
    191     }
    192 
    193 
    194     @Override
    195     protected void onAttachedToWindow() {
    196         super.onAttachedToWindow();
    197         final ViewTreeObserver treeObserver = getViewTreeObserver();
    198         treeObserver.addOnTouchModeChangeListener(this);
    199     }
    200 
    201     @Override
    202     protected void onDetachedFromWindow() {
    203         super.onDetachedFromWindow();
    204         final ViewTreeObserver treeObserver = getViewTreeObserver();
    205         treeObserver.removeOnTouchModeChangeListener(this);
    206     }
    207 
    208     /**
    209      * {@inheritDoc}
    210      */
    211     public void onTouchModeChanged(boolean isInTouchMode) {
    212         if (!isInTouchMode) {
    213             // leaving touch mode.. if nothing has focus, let's give it to
    214             // the indicator of the current tab
    215             if (mCurrentView != null && (!mCurrentView.hasFocus() || mCurrentView.isFocused())) {
    216                 mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
    217             }
    218         }
    219     }
    220 
    221     /**
    222      * Add a tab.
    223      * @param tabSpec Specifies how to create the indicator and content.
    224      */
    225     public void addTab(TabSpec tabSpec) {
    226 
    227         if (tabSpec.mIndicatorStrategy == null) {
    228             throw new IllegalArgumentException("you must specify a way to create the tab indicator.");
    229         }
    230 
    231         if (tabSpec.mContentStrategy == null) {
    232             throw new IllegalArgumentException("you must specify a way to create the tab content");
    233         }
    234         View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();
    235         tabIndicator.setOnKeyListener(mTabKeyListener);
    236 
    237         // If this is a custom view, then do not draw the bottom strips for
    238         // the tab indicators.
    239         if (tabSpec.mIndicatorStrategy instanceof ViewIndicatorStrategy) {
    240             mTabWidget.setStripEnabled(false);
    241         }
    242 
    243         mTabWidget.addView(tabIndicator);
    244         mTabSpecs.add(tabSpec);
    245 
    246         if (mCurrentTab == -1) {
    247             setCurrentTab(0);
    248         }
    249     }
    250 
    251 
    252     /**
    253      * Removes all tabs from the tab widget associated with this tab host.
    254      */
    255     public void clearAllTabs() {
    256         mTabWidget.removeAllViews();
    257         initTabHost();
    258         mTabContent.removeAllViews();
    259         mTabSpecs.clear();
    260         requestLayout();
    261         invalidate();
    262     }
    263 
    264     public TabWidget getTabWidget() {
    265         return mTabWidget;
    266     }
    267 
    268     public int getCurrentTab() {
    269         return mCurrentTab;
    270     }
    271 
    272     public String getCurrentTabTag() {
    273         if (mCurrentTab >= 0 && mCurrentTab < mTabSpecs.size()) {
    274             return mTabSpecs.get(mCurrentTab).getTag();
    275         }
    276         return null;
    277     }
    278 
    279     public View getCurrentTabView() {
    280         if (mCurrentTab >= 0 && mCurrentTab < mTabSpecs.size()) {
    281             return mTabWidget.getChildTabViewAt(mCurrentTab);
    282         }
    283         return null;
    284     }
    285 
    286     public View getCurrentView() {
    287         return mCurrentView;
    288     }
    289 
    290     public void setCurrentTabByTag(String tag) {
    291         int i;
    292         for (i = 0; i < mTabSpecs.size(); i++) {
    293             if (mTabSpecs.get(i).getTag().equals(tag)) {
    294                 setCurrentTab(i);
    295                 break;
    296             }
    297         }
    298     }
    299 
    300     /**
    301      * Get the FrameLayout which holds tab content
    302      */
    303     public FrameLayout getTabContentView() {
    304         return mTabContent;
    305     }
    306 
    307     /**
    308      * Get the location of the TabWidget.
    309      *
    310      * @return The TabWidget location.
    311      */
    312     private int getTabWidgetLocation() {
    313         int location = TABWIDGET_LOCATION_TOP;
    314 
    315         switch (mTabWidget.getOrientation()) {
    316             case LinearLayout.VERTICAL:
    317                 location = (mTabContent.getLeft() < mTabWidget.getLeft()) ? TABWIDGET_LOCATION_RIGHT
    318                         : TABWIDGET_LOCATION_LEFT;
    319                 break;
    320             case LinearLayout.HORIZONTAL:
    321             default:
    322                 location = (mTabContent.getTop() < mTabWidget.getTop()) ? TABWIDGET_LOCATION_BOTTOM
    323                         : TABWIDGET_LOCATION_TOP;
    324                 break;
    325         }
    326         return location;
    327     }
    328 
    329     @Override
    330     public boolean dispatchKeyEvent(KeyEvent event) {
    331         final boolean handled = super.dispatchKeyEvent(event);
    332 
    333         // unhandled key events change focus to tab indicator for embedded
    334         // activities when there is nothing that will take focus from default
    335         // focus searching
    336         if (!handled
    337                 && (event.getAction() == KeyEvent.ACTION_DOWN)
    338                 && (mCurrentView != null)
    339                 && (mCurrentView.isRootNamespace())
    340                 && (mCurrentView.hasFocus())) {
    341             int keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
    342             int directionShouldChangeFocus = View.FOCUS_UP;
    343             int soundEffect = SoundEffectConstants.NAVIGATION_UP;
    344 
    345             switch (getTabWidgetLocation()) {
    346                 case TABWIDGET_LOCATION_LEFT:
    347                     keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_LEFT;
    348                     directionShouldChangeFocus = View.FOCUS_LEFT;
    349                     soundEffect = SoundEffectConstants.NAVIGATION_LEFT;
    350                     break;
    351                 case TABWIDGET_LOCATION_RIGHT:
    352                     keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_RIGHT;
    353                     directionShouldChangeFocus = View.FOCUS_RIGHT;
    354                     soundEffect = SoundEffectConstants.NAVIGATION_RIGHT;
    355                     break;
    356                 case TABWIDGET_LOCATION_BOTTOM:
    357                     keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_DOWN;
    358                     directionShouldChangeFocus = View.FOCUS_DOWN;
    359                     soundEffect = SoundEffectConstants.NAVIGATION_DOWN;
    360                     break;
    361                 case TABWIDGET_LOCATION_TOP:
    362                 default:
    363                     keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
    364                     directionShouldChangeFocus = View.FOCUS_UP;
    365                     soundEffect = SoundEffectConstants.NAVIGATION_UP;
    366                     break;
    367             }
    368             if (event.getKeyCode() == keyCodeShouldChangeFocus
    369                     && mCurrentView.findFocus().focusSearch(directionShouldChangeFocus) == null) {
    370                 mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
    371                 playSoundEffect(soundEffect);
    372                 return true;
    373             }
    374         }
    375         return handled;
    376     }
    377 
    378 
    379     @Override
    380     public void dispatchWindowFocusChanged(boolean hasFocus) {
    381         if (mCurrentView != null){
    382             mCurrentView.dispatchWindowFocusChanged(hasFocus);
    383         }
    384     }
    385 
    386     @Override
    387     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    388         super.onInitializeAccessibilityEvent(event);
    389         event.setClassName(TabHost.class.getName());
    390     }
    391 
    392     @Override
    393     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    394         super.onInitializeAccessibilityNodeInfo(info);
    395         info.setClassName(TabHost.class.getName());
    396     }
    397 
    398     public void setCurrentTab(int index) {
    399         if (index < 0 || index >= mTabSpecs.size()) {
    400             return;
    401         }
    402 
    403         if (index == mCurrentTab) {
    404             return;
    405         }
    406 
    407         // notify old tab content
    408         if (mCurrentTab != -1) {
    409             mTabSpecs.get(mCurrentTab).mContentStrategy.tabClosed();
    410         }
    411 
    412         mCurrentTab = index;
    413         final TabHost.TabSpec spec = mTabSpecs.get(index);
    414 
    415         // Call the tab widget's focusCurrentTab(), instead of just
    416         // selecting the tab.
    417         mTabWidget.focusCurrentTab(mCurrentTab);
    418 
    419         // tab content
    420         mCurrentView = spec.mContentStrategy.getContentView();
    421 
    422         if (mCurrentView.getParent() == null) {
    423             mTabContent
    424                     .addView(
    425                             mCurrentView,
    426                             new ViewGroup.LayoutParams(
    427                                     ViewGroup.LayoutParams.MATCH_PARENT,
    428                                     ViewGroup.LayoutParams.MATCH_PARENT));
    429         }
    430 
    431         if (!mTabWidget.hasFocus()) {
    432             // if the tab widget didn't take focus (likely because we're in touch mode)
    433             // give the current tab content view a shot
    434             mCurrentView.requestFocus();
    435         }
    436 
    437         //mTabContent.requestFocus(View.FOCUS_FORWARD);
    438         invokeOnTabChangeListener();
    439     }
    440 
    441     /**
    442      * Register a callback to be invoked when the selected state of any of the items
    443      * in this list changes
    444      * @param l
    445      * The callback that will run
    446      */
    447     public void setOnTabChangedListener(OnTabChangeListener l) {
    448         mOnTabChangeListener = l;
    449     }
    450 
    451     private void invokeOnTabChangeListener() {
    452         if (mOnTabChangeListener != null) {
    453             mOnTabChangeListener.onTabChanged(getCurrentTabTag());
    454         }
    455     }
    456 
    457     /**
    458      * Interface definition for a callback to be invoked when tab changed
    459      */
    460     public interface OnTabChangeListener {
    461         void onTabChanged(String tabId);
    462     }
    463 
    464 
    465     /**
    466      * Makes the content of a tab when it is selected. Use this if your tab
    467      * content needs to be created on demand, i.e. you are not showing an
    468      * existing view or starting an activity.
    469      */
    470     public interface TabContentFactory {
    471         /**
    472          * Callback to make the tab contents
    473          *
    474          * @param tag
    475          *            Which tab was selected.
    476          * @return The view to display the contents of the selected tab.
    477          */
    478         View createTabContent(String tag);
    479     }
    480 
    481 
    482     /**
    483      * A tab has a tab indicator, content, and a tag that is used to keep
    484      * track of it.  This builder helps choose among these options.
    485      *
    486      * For the tab indicator, your choices are:
    487      * 1) set a label
    488      * 2) set a label and an icon
    489      *
    490      * For the tab content, your choices are:
    491      * 1) the id of a {@link View}
    492      * 2) a {@link TabContentFactory} that creates the {@link View} content.
    493      * 3) an {@link Intent} that launches an {@link android.app.Activity}.
    494      */
    495     public class TabSpec {
    496 
    497         private String mTag;
    498 
    499         private IndicatorStrategy mIndicatorStrategy;
    500         private ContentStrategy mContentStrategy;
    501 
    502         private TabSpec(String tag) {
    503             mTag = tag;
    504         }
    505 
    506         /**
    507          * Specify a label as the tab indicator.
    508          */
    509         public TabSpec setIndicator(CharSequence label) {
    510             mIndicatorStrategy = new LabelIndicatorStrategy(label);
    511             return this;
    512         }
    513 
    514         /**
    515          * Specify a label and icon as the tab indicator.
    516          */
    517         public TabSpec setIndicator(CharSequence label, Drawable icon) {
    518             mIndicatorStrategy = new LabelAndIconIndicatorStrategy(label, icon);
    519             return this;
    520         }
    521 
    522         /**
    523          * Specify a view as the tab indicator.
    524          */
    525         public TabSpec setIndicator(View view) {
    526             mIndicatorStrategy = new ViewIndicatorStrategy(view);
    527             return this;
    528         }
    529 
    530         /**
    531          * Specify the id of the view that should be used as the content
    532          * of the tab.
    533          */
    534         public TabSpec setContent(int viewId) {
    535             mContentStrategy = new ViewIdContentStrategy(viewId);
    536             return this;
    537         }
    538 
    539         /**
    540          * Specify a {@link android.widget.TabHost.TabContentFactory} to use to
    541          * create the content of the tab.
    542          */
    543         public TabSpec setContent(TabContentFactory contentFactory) {
    544             mContentStrategy = new FactoryContentStrategy(mTag, contentFactory);
    545             return this;
    546         }
    547 
    548         /**
    549          * Specify an intent to use to launch an activity as the tab content.
    550          */
    551         public TabSpec setContent(Intent intent) {
    552             mContentStrategy = new IntentContentStrategy(mTag, intent);
    553             return this;
    554         }
    555 
    556 
    557         public String getTag() {
    558             return mTag;
    559         }
    560     }
    561 
    562     /**
    563      * Specifies what you do to create a tab indicator.
    564      */
    565     private static interface IndicatorStrategy {
    566 
    567         /**
    568          * Return the view for the indicator.
    569          */
    570         View createIndicatorView();
    571     }
    572 
    573     /**
    574      * Specifies what you do to manage the tab content.
    575      */
    576     private static interface ContentStrategy {
    577 
    578         /**
    579          * Return the content view.  The view should may be cached locally.
    580          */
    581         View getContentView();
    582 
    583         /**
    584          * Perhaps do something when the tab associated with this content has
    585          * been closed (i.e make it invisible, or remove it).
    586          */
    587         void tabClosed();
    588     }
    589 
    590     /**
    591      * How to create a tab indicator that just has a label.
    592      */
    593     private class LabelIndicatorStrategy implements IndicatorStrategy {
    594 
    595         private final CharSequence mLabel;
    596 
    597         private LabelIndicatorStrategy(CharSequence label) {
    598             mLabel = label;
    599         }
    600 
    601         public View createIndicatorView() {
    602             final Context context = getContext();
    603             LayoutInflater inflater =
    604                     (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    605             View tabIndicator = inflater.inflate(mTabLayoutId,
    606                     mTabWidget, // tab widget is the parent
    607                     false); // no inflate params
    608 
    609             final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
    610             tv.setText(mLabel);
    611 
    612             if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
    613                 // Donut apps get old color scheme
    614                 tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
    615                 tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
    616             }
    617 
    618             return tabIndicator;
    619         }
    620     }
    621 
    622     /**
    623      * How we create a tab indicator that has a label and an icon
    624      */
    625     private class LabelAndIconIndicatorStrategy implements IndicatorStrategy {
    626 
    627         private final CharSequence mLabel;
    628         private final Drawable mIcon;
    629 
    630         private LabelAndIconIndicatorStrategy(CharSequence label, Drawable icon) {
    631             mLabel = label;
    632             mIcon = icon;
    633         }
    634 
    635         public View createIndicatorView() {
    636             final Context context = getContext();
    637             LayoutInflater inflater =
    638                     (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    639             View tabIndicator = inflater.inflate(mTabLayoutId,
    640                     mTabWidget, // tab widget is the parent
    641                     false); // no inflate params
    642 
    643             final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
    644             final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);
    645 
    646             // when icon is gone by default, we're in exclusive mode
    647             final boolean exclusive = iconView.getVisibility() == View.GONE;
    648             final boolean bindIcon = !exclusive || TextUtils.isEmpty(mLabel);
    649 
    650             tv.setText(mLabel);
    651 
    652             if (bindIcon && mIcon != null) {
    653                 iconView.setImageDrawable(mIcon);
    654                 iconView.setVisibility(VISIBLE);
    655             }
    656 
    657             if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
    658                 // Donut apps get old color scheme
    659                 tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
    660                 tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
    661             }
    662 
    663             return tabIndicator;
    664         }
    665     }
    666 
    667     /**
    668      * How to create a tab indicator by specifying a view.
    669      */
    670     private class ViewIndicatorStrategy implements IndicatorStrategy {
    671 
    672         private final View mView;
    673 
    674         private ViewIndicatorStrategy(View view) {
    675             mView = view;
    676         }
    677 
    678         public View createIndicatorView() {
    679             return mView;
    680         }
    681     }
    682 
    683     /**
    684      * How to create the tab content via a view id.
    685      */
    686     private class ViewIdContentStrategy implements ContentStrategy {
    687 
    688         private final View mView;
    689 
    690         private ViewIdContentStrategy(int viewId) {
    691             mView = mTabContent.findViewById(viewId);
    692             if (mView != null) {
    693                 mView.setVisibility(View.GONE);
    694             } else {
    695                 throw new RuntimeException("Could not create tab content because " +
    696                         "could not find view with id " + viewId);
    697             }
    698         }
    699 
    700         public View getContentView() {
    701             mView.setVisibility(View.VISIBLE);
    702             return mView;
    703         }
    704 
    705         public void tabClosed() {
    706             mView.setVisibility(View.GONE);
    707         }
    708     }
    709 
    710     /**
    711      * How tab content is managed using {@link TabContentFactory}.
    712      */
    713     private class FactoryContentStrategy implements ContentStrategy {
    714         private View mTabContent;
    715         private final CharSequence mTag;
    716         private TabContentFactory mFactory;
    717 
    718         public FactoryContentStrategy(CharSequence tag, TabContentFactory factory) {
    719             mTag = tag;
    720             mFactory = factory;
    721         }
    722 
    723         public View getContentView() {
    724             if (mTabContent == null) {
    725                 mTabContent = mFactory.createTabContent(mTag.toString());
    726             }
    727             mTabContent.setVisibility(View.VISIBLE);
    728             return mTabContent;
    729         }
    730 
    731         public void tabClosed() {
    732             mTabContent.setVisibility(View.GONE);
    733         }
    734     }
    735 
    736     /**
    737      * How tab content is managed via an {@link Intent}: the content view is the
    738      * decorview of the launched activity.
    739      */
    740     private class IntentContentStrategy implements ContentStrategy {
    741 
    742         private final String mTag;
    743         private final Intent mIntent;
    744 
    745         private View mLaunchedView;
    746 
    747         private IntentContentStrategy(String tag, Intent intent) {
    748             mTag = tag;
    749             mIntent = intent;
    750         }
    751 
    752         public View getContentView() {
    753             if (mLocalActivityManager == null) {
    754                 throw new IllegalStateException("Did you forget to call 'public void setup(LocalActivityManager activityGroup)'?");
    755             }
    756             final Window w = mLocalActivityManager.startActivity(
    757                     mTag, mIntent);
    758             final View wd = w != null ? w.getDecorView() : null;
    759             if (mLaunchedView != wd && mLaunchedView != null) {
    760                 if (mLaunchedView.getParent() != null) {
    761                     mTabContent.removeView(mLaunchedView);
    762                 }
    763             }
    764             mLaunchedView = wd;
    765 
    766             // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activities for now so they can get
    767             // focus if none of their children have it. They need focus to be able to
    768             // display menu items.
    769             //
    770             // Replace this with something better when Bug 628886 is fixed...
    771             //
    772             if (mLaunchedView != null) {
    773                 mLaunchedView.setVisibility(View.VISIBLE);
    774                 mLaunchedView.setFocusableInTouchMode(true);
    775                 ((ViewGroup) mLaunchedView).setDescendantFocusability(
    776                         FOCUS_AFTER_DESCENDANTS);
    777             }
    778             return mLaunchedView;
    779         }
    780 
    781         public void tabClosed() {
    782             if (mLaunchedView != null) {
    783                 mLaunchedView.setVisibility(View.GONE);
    784             }
    785         }
    786     }
    787 
    788 }
    789