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