• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TabHost(Context context)74     public TabHost(Context context) {
75         super(context);
76         initTabHost();
77     }
78 
TabHost(Context context, AttributeSet attrs)79     public TabHost(Context context, AttributeSet attrs) {
80         this(context, attrs, com.android.internal.R.attr.tabWidgetStyle);
81     }
82 
TabHost(Context context, AttributeSet attrs, int defStyleAttr)83     public TabHost(Context context, AttributeSet attrs, int defStyleAttr) {
84         this(context, attrs, defStyleAttr, 0);
85     }
86 
TabHost(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 
initTabHost()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      */
newTabSpec(String tag)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       */
setup()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
sendAccessibilityEvent(int eventType)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      */
setup(LocalActivityManager activityGroup)188     public void setup(LocalActivityManager activityGroup) {
189         setup();
190         mLocalActivityManager = activityGroup;
191     }
192 
193 
194     @Override
onAttachedToWindow()195     protected void onAttachedToWindow() {
196         super.onAttachedToWindow();
197         final ViewTreeObserver treeObserver = getViewTreeObserver();
198         treeObserver.addOnTouchModeChangeListener(this);
199     }
200 
201     @Override
onDetachedFromWindow()202     protected void onDetachedFromWindow() {
203         super.onDetachedFromWindow();
204         final ViewTreeObserver treeObserver = getViewTreeObserver();
205         treeObserver.removeOnTouchModeChangeListener(this);
206     }
207 
208     /**
209      * {@inheritDoc}
210      */
onTouchModeChanged(boolean isInTouchMode)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      */
addTab(TabSpec tabSpec)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      */
clearAllTabs()255     public void clearAllTabs() {
256         mTabWidget.removeAllViews();
257         initTabHost();
258         mTabContent.removeAllViews();
259         mTabSpecs.clear();
260         requestLayout();
261         invalidate();
262     }
263 
getTabWidget()264     public TabWidget getTabWidget() {
265         return mTabWidget;
266     }
267 
getCurrentTab()268     public int getCurrentTab() {
269         return mCurrentTab;
270     }
271 
getCurrentTabTag()272     public String getCurrentTabTag() {
273         if (mCurrentTab >= 0 && mCurrentTab < mTabSpecs.size()) {
274             return mTabSpecs.get(mCurrentTab).getTag();
275         }
276         return null;
277     }
278 
getCurrentTabView()279     public View getCurrentTabView() {
280         if (mCurrentTab >= 0 && mCurrentTab < mTabSpecs.size()) {
281             return mTabWidget.getChildTabViewAt(mCurrentTab);
282         }
283         return null;
284     }
285 
getCurrentView()286     public View getCurrentView() {
287         return mCurrentView;
288     }
289 
setCurrentTabByTag(String tag)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      */
getTabContentView()303     public FrameLayout getTabContentView() {
304         return mTabContent;
305     }
306 
307     /**
308      * Get the location of the TabWidget.
309      *
310      * @return The TabWidget location.
311      */
getTabWidgetLocation()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
dispatchKeyEvent(KeyEvent event)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
dispatchWindowFocusChanged(boolean hasFocus)380     public void dispatchWindowFocusChanged(boolean hasFocus) {
381         if (mCurrentView != null){
382             mCurrentView.dispatchWindowFocusChanged(hasFocus);
383         }
384     }
385 
386     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)387     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
388         super.onInitializeAccessibilityEvent(event);
389         event.setClassName(TabHost.class.getName());
390     }
391 
392     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)393     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
394         super.onInitializeAccessibilityNodeInfo(info);
395         info.setClassName(TabHost.class.getName());
396     }
397 
setCurrentTab(int index)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      */
setOnTabChangedListener(OnTabChangeListener l)447     public void setOnTabChangedListener(OnTabChangeListener l) {
448         mOnTabChangeListener = l;
449     }
450 
invokeOnTabChangeListener()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 {
onTabChanged(String tabId)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          */
createTabContent(String tag)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 
TabSpec(String tag)502         private TabSpec(String tag) {
503             mTag = tag;
504         }
505 
506         /**
507          * Specify a label as the tab indicator.
508          */
setIndicator(CharSequence label)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          */
setIndicator(CharSequence label, Drawable icon)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          */
setIndicator(View view)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          */
setContent(int viewId)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          */
setContent(TabContentFactory contentFactory)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          */
setContent(Intent intent)551         public TabSpec setContent(Intent intent) {
552             mContentStrategy = new IntentContentStrategy(mTag, intent);
553             return this;
554         }
555 
556 
getTag()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          */
createIndicatorView()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          */
getContentView()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          */
tabClosed()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 
LabelIndicatorStrategy(CharSequence label)597         private LabelIndicatorStrategy(CharSequence label) {
598             mLabel = label;
599         }
600 
createIndicatorView()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 
LabelAndIconIndicatorStrategy(CharSequence label, Drawable icon)630         private LabelAndIconIndicatorStrategy(CharSequence label, Drawable icon) {
631             mLabel = label;
632             mIcon = icon;
633         }
634 
createIndicatorView()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 
ViewIndicatorStrategy(View view)674         private ViewIndicatorStrategy(View view) {
675             mView = view;
676         }
677 
createIndicatorView()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 
ViewIdContentStrategy(int viewId)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 
getContentView()700         public View getContentView() {
701             mView.setVisibility(View.VISIBLE);
702             return mView;
703         }
704 
tabClosed()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 
FactoryContentStrategy(CharSequence tag, TabContentFactory factory)718         public FactoryContentStrategy(CharSequence tag, TabContentFactory factory) {
719             mTag = tag;
720             mFactory = factory;
721         }
722 
getContentView()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 
tabClosed()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 
IntentContentStrategy(String tag, Intent intent)747         private IntentContentStrategy(String tag, Intent intent) {
748             mTag = tag;
749             mIntent = intent;
750         }
751 
getContentView()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 
tabClosed()781         public void tabClosed() {
782             if (mLaunchedView != null) {
783                 mLaunchedView.setVisibility(View.GONE);
784             }
785         }
786     }
787 
788 }
789