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