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
17package android.widget;
18
19import com.android.internal.R;
20
21import android.app.LocalActivityManager;
22import android.content.Context;
23import android.content.Intent;
24import android.content.res.TypedArray;
25import android.graphics.drawable.Drawable;
26import android.os.Build;
27import android.text.TextUtils;
28import android.util.AttributeSet;
29import android.view.KeyEvent;
30import android.view.LayoutInflater;
31import android.view.SoundEffectConstants;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.ViewTreeObserver;
35import android.view.Window;
36import android.view.accessibility.AccessibilityEvent;
37import android.view.accessibility.AccessibilityNodeInfo;
38
39import java.util.ArrayList;
40import 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 */
49public 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);
122mTabHost.setup();
123mTabHost.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