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