19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guyimport android.R;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
217609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleronimport android.content.res.Resources;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
267609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleronimport android.os.Build;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View.OnFocusChangeListener;
3097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunneimport android.view.ViewGroup;
315ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
328a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
353730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar *
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Displays a list of tab labels representing each page in the parent's tab
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * collection. The container object for this widget is
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link android.widget.TabHost TabHost}. When the user selects a tab, this
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * object sends a message to the parent container, TabHost, to tell it to switch
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the displayed page. You typically won't use many methods directly on this
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * object. The container TabHost is used to add labels, add the callback
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * handler, and manage callbacks. You might call this object to iterate the list
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of tabs, or to tweak the layout of the tab list, but most methods should be
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * called on the containing TabHost object.
457883c975dd0db021fa3a01226d6cb09c28764c98Romain Guy *
467883c975dd0db021fa3a01226d6cb09c28764c98Romain Guy * @attr ref android.R.styleable#TabWidget_divider
476b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripEnabled
486b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripLeft
496b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripRight
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class TabWidget extends LinearLayout implements OnFocusChangeListener {
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnTabSelectionChanged mSelectionChangedListener;
5361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
5497dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne    // This value will be set to 0 as soon as the first tab is added to TabHost.
5597dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne    private int mSelectedTab = -1;
5661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
5765fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy    private Drawable mLeftStrip;
5865fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy    private Drawable mRightStrip;
5961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
6061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    private boolean mDrawBottomStrips = true;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mStripMoved;
6261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
6361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    private final Rect mBounds = new Rect();
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
65c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    // When positive, the widths and heights of tabs will be imposed so that they fit in parent
66c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    private int mImposedTabsHeight = -1;
67c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    private int[] mImposedTabWidths;
68c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public TabWidget(Context context) {
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, null);
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public TabWidget(Context context, AttributeSet attrs) {
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, attrs, com.android.internal.R.attr.tabWidgetStyle);
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public TabWidget(Context context, AttributeSet attrs, int defStyle) {
7811f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        super(context, attrs, defStyle);
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8011f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        final TypedArray a = context.obtainStyledAttributes(
8111f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey                attrs, com.android.internal.R.styleable.TabWidget, defStyle, 0);
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8311f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        setStripEnabled(a.getBoolean(R.styleable.TabWidget_tabStripEnabled, true));
8411f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        setLeftStripDrawable(a.getDrawable(R.styleable.TabWidget_tabStripLeft));
8511f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        setRightStripDrawable(a.getDrawable(R.styleable.TabWidget_tabStripRight));
8661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        a.recycle();
8861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
8961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        initTabWidget();
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
913730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mStripMoved = true;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onSizeChanged(w, h, oldw, oldh);
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
983730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar    @Override
993730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar    protected int getChildDrawingOrder(int childCount, int i) {
10097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne        if (mSelectedTab == -1) {
1013730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar            return i;
10297dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne        } else {
10397dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            // Always draw the selected tab last, so that drop shadows are drawn
10497dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            // in the correct z-order.
10597dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            if (i == childCount - 1) {
10697dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne                return mSelectedTab;
10797dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            } else if (i >= mSelectedTab) {
10897dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne                return i + 1;
10997dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            } else {
11097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne                return i;
11197dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            }
1123730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar        }
1133730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar    }
1143730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void initTabWidget() {
11611f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        setChildrenDrawingOrderEnabled(true);
1173730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
1187609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron        final Context context = mContext;
1197609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron        final Resources resources = context.getResources();
12044c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne
12144c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne        // Tests the target Sdk version, as set in the Manifest. Could not be set using styles.xml
12244c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne        // in a values-v? directory which targets the current platform Sdk version instead.
1237609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
1247609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron            // Donut apps get old color scheme
12565fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy            if (mLeftStrip == null) {
12665fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy                mLeftStrip = resources.getDrawable(
12761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy                        com.android.internal.R.drawable.tab_bottom_left_v4);
12861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            }
12965fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy            if (mRightStrip == null) {
13065fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy                mRightStrip = resources.getDrawable(
13161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy                        com.android.internal.R.drawable.tab_bottom_right_v4);
13261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            }
1331ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn        } else {
1341ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            // Use modern color scheme for Eclair and beyond
1351ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            if (mLeftStrip == null) {
1361ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn                mLeftStrip = resources.getDrawable(
1371ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn                        com.android.internal.R.drawable.tab_bottom_left);
1381ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            }
1391ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            if (mRightStrip == null) {
1401ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn                mRightStrip = resources.getDrawable(
1411ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn                        com.android.internal.R.drawable.tab_bottom_right);
1421ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            }
14311f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        }
1447609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Deal with focus, as we don't want the focus to go by default
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // to a tab other than the current tab
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setFocusable(true);
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setOnFocusChangeListener(this);
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
151c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    @Override
152c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    void measureChildBeforeLayout(View child, int childIndex,
153c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            int widthMeasureSpec, int totalWidth,
154c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            int heightMeasureSpec, int totalHeight) {
15511f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        if (!isMeasureWithLargestChildEnabled() && mImposedTabsHeight >= 0) {
156c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            widthMeasureSpec = MeasureSpec.makeMeasureSpec(
157c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    totalWidth + mImposedTabWidths[childIndex], MeasureSpec.EXACTLY);
158c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            heightMeasureSpec = MeasureSpec.makeMeasureSpec(mImposedTabsHeight,
159c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    MeasureSpec.EXACTLY);
160c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        }
161c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
162c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        super.measureChildBeforeLayout(child, childIndex,
163c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight);
164c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    }
165c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
1666741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    @Override
1676741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
16852a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) {
16952a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne            super.measureHorizontal(widthMeasureSpec, heightMeasureSpec);
17052a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne            return;
17152a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne        }
17252a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne
173c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        // First, measure with no constraint
1746741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne        final int unspecifiedWidth = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
175c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        mImposedTabsHeight = -1;
176cd59febcea2b75ebe657786133dcfa81fc3d5eb1Gilles Debunne        super.measureHorizontal(unspecifiedWidth, heightMeasureSpec);
1776741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne
178c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        int extraWidth = getMeasuredWidth() - MeasureSpec.getSize(widthMeasureSpec);
179c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        if (extraWidth > 0) {
180c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            final int count = getChildCount();
1816741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne
182c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            int childCount = 0;
1836741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne            for (int i = 0; i < count; i++) {
1846741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne                final View child = getChildAt(i);
185c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                if (child.getVisibility() == GONE) continue;
186c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                childCount++;
187c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            }
1886741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne
189c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            if (childCount > 0) {
190c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                if (mImposedTabWidths == null || mImposedTabWidths.length != count) {
191c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    mImposedTabWidths = new int[count];
192c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                }
193c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                for (int i = 0; i < count; i++) {
194c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    final View child = getChildAt(i);
195c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    if (child.getVisibility() == GONE) continue;
196c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    final int childWidth = child.getMeasuredWidth();
197c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    final int delta = extraWidth / childCount;
198c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    final int newWidth = Math.max(0, childWidth - delta);
199c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    mImposedTabWidths[i] = newWidth;
200c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    // Make sure the extra width is evenly distributed, no int division remainder
201c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    extraWidth -= childWidth - newWidth; // delta may have been clamped
202c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    childCount--;
203c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    mImposedTabsHeight = Math.max(mImposedTabsHeight, child.getMeasuredHeight());
204c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                }
2056741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne            }
206c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        }
207c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
208c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        // Measure again, this time with imposed tab widths and respecting initial spec request
20952a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne        super.measureHorizontal(widthMeasureSpec, heightMeasureSpec);
2106741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    }
2116741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne
2126741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    /**
21353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Returns the tab indicator view at the given index.
21453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     *
21553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @param index the zero-based index of the tab indicator view to return
21653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @return the tab indicator view at the given index
21753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
21853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    public View getChildTabViewAt(int index) {
21953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        return getChildAt(index);
22053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
22153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
22253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    /**
22353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Returns the number of tab indicator views.
22453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @return the number of tab indicator views.
22553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
22653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    public int getTabCount() {
22711f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        return getChildCount();
22853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
22953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
23053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    /**
23153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Sets the drawable to use as a divider between the tab indicators.
23253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @param drawable the divider drawable
23353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
2346741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    @Override
23553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    public void setDividerDrawable(Drawable drawable) {
23611f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        super.setDividerDrawable(drawable);
23753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
23853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
23953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    /**
24053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Sets the drawable to use as a divider between the tab indicators.
24153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @param resId the resource identifier of the drawable to use as a
24253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * divider.
24353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
24453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    public void setDividerDrawable(int resId) {
24511f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        setDividerDrawable(getResources().getDrawable(resId));
24653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
24761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
24861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
24961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * Sets the drawable to use as the left part of the strip below the
25061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * tab indicators.
25161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * @param drawable the left strip drawable
25261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
25361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setLeftStripDrawable(Drawable drawable) {
25465fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        mLeftStrip = drawable;
25542c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        requestLayout();
25642c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();
25761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    }
25861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
25961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
26061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * Sets the drawable to use as the left part of the strip below the
26161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * tab indicators.
26261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * @param resId the resource identifier of the drawable to use as the
26361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * left strip drawable
26461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
26561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setLeftStripDrawable(int resId) {
26611f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        setLeftStripDrawable(getResources().getDrawable(resId));
26761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    }
26861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
26961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
270189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen     * Sets the drawable to use as the right part of the strip below the
27161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * tab indicators.
272189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen     * @param drawable the right strip drawable
27361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
27461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setRightStripDrawable(Drawable drawable) {
27565fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        mRightStrip = drawable;
27642c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        requestLayout();
27711f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        invalidate();
27811f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey    }
27953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
28053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    /**
281189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen     * Sets the drawable to use as the right part of the strip below the
28261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * tab indicators.
28361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * @param resId the resource identifier of the drawable to use as the
284189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen     * right strip drawable
28561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
28661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setRightStripDrawable(int resId) {
28711f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        setRightStripDrawable(getResources().getDrawable(resId));
28861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    }
28911f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey
29061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
29153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Controls whether the bottom strips on the tab indicators are drawn or
29253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * not.  The default is to draw them.  If the user specifies a custom
29353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * view for the tab indicators, then the TabHost class calls this method
29453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * to disable drawing of the bottom strips.
29561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * @param stripEnabled true if the bottom strips should be drawn.
29661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
29761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setStripEnabled(boolean stripEnabled) {
29861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        mDrawBottomStrips = stripEnabled;
29942c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();
30061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    }
30161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
30261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
30361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * Indicates whether the bottom strips on the tab indicators are drawn
30461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * or not.
30553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
30661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public boolean isStripEnabled() {
30761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        return mDrawBottomStrips;
30853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
30953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void childDrawableStateChanged(View child) {
312acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert        if (getTabCount() > 0 && child == getChildTabViewAt(mSelectedTab)) {
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // To make sure that the bottom strip is redrawn
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            invalidate();
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.childDrawableStateChanged(child);
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3183730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void dispatchDraw(Canvas canvas) {
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.dispatchDraw(canvas);
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
323acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert        // Do nothing if there are no tabs.
324acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert        if (getTabCount() == 0) return;
325acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert
32653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // If the user specified a custom view for the tab indicators, then
32753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // do not draw the bottom strips.
32853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        if (!mDrawBottomStrips) {
32953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            // Skip drawing the bottom strips.
33053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            return;
33153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        }
33253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
33361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        final View selectedChild = getChildTabViewAt(mSelectedTab);
33461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
33565fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        final Drawable leftStrip = mLeftStrip;
33665fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        final Drawable rightStrip = mRightStrip;
3373730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
33861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        leftStrip.setState(selectedChild.getDrawableState());
33961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        rightStrip.setState(selectedChild.getDrawableState());
3403730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mStripMoved) {
34261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            final Rect bounds = mBounds;
34361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            bounds.left = selectedChild.getLeft();
34461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            bounds.right = selectedChild.getRight();
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int myHeight = getHeight();
34661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            leftStrip.setBounds(Math.min(0, bounds.left - leftStrip.getIntrinsicWidth()),
34761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy                    myHeight - leftStrip.getIntrinsicHeight(), bounds.left, myHeight);
34861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            rightStrip.setBounds(bounds.right, myHeight - rightStrip.getIntrinsicHeight(),
34961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy                    Math.max(getWidth(), bounds.right + rightStrip.getIntrinsicWidth()), myHeight);
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStripMoved = false;
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3523730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
35361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        leftStrip.draw(canvas);
35461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        rightStrip.draw(canvas);
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Sets the current tab.
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method is used to bring a tab to the front of the Widget,
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and is used to post to the rest of the UI that a different tab
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * has been brought to the foreground.
3623730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
3633730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * Note, this is separate from the traditional "focus" that is
3643730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * employed from the view logic.
3653730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
3663730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * For instance, if we have a list in a tabbed view, a user may be
3673730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * navigating up and down the list, moving the UI focus (orange
3683730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * highlighting) through the list items.  The cursor movement does
3693730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * not effect the "selected" tab though, because what is being
3703730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * scrolled through is all on the same tab.  The selected tab only
3713730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * changes when we navigate between tabs (moving from the list view
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to the next tabbed view, in this example).
3733730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * To move both the focus AND the selected tab at once, please use
3753730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * {@link #setCurrentTab}. Normally, the view logic takes care of
3763730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * adjusting the focus, so unless you're circumventing the UI,
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * you'll probably just focus your interest here.
3783730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  @param index The tab that you want to indicate as the selected
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  tab (tab brought to the front of the widget)
3813730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  @see #focusCurrentTab
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setCurrentTab(int index) {
3855ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        if (index < 0 || index >= getTabCount() || index == mSelectedTab) {
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
38997dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne        if (mSelectedTab != -1) {
39097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            getChildTabViewAt(mSelectedTab).setSelected(false);
39197dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne        }
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSelectedTab = index;
39353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        getChildTabViewAt(mSelectedTab).setSelected(true);
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mStripMoved = true;
3955ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov
3965ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        if (isShown()) {
3975ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
3985ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        }
3995ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    }
4005ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov
4015ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    @Override
4025ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
403736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        onPopulateAccessibilityEvent(event);
404736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        // Dispatch only to the selected tab.
4053fb3d7c4e756bd32d5abde0abca9ab52d559bc84Adam Powell        if (mSelectedTab != -1) {
4060b0a41d8e26eaf0f1d9d922621494daf40964a9aSvetoslav Ganov            View tabView = getChildTabViewAt(mSelectedTab);
4070b0a41d8e26eaf0f1d9d922621494daf40964a9aSvetoslav Ganov            if (tabView != null && tabView.getVisibility() == VISIBLE) {
4080b0a41d8e26eaf0f1d9d922621494daf40964a9aSvetoslav Ganov                return tabView.dispatchPopulateAccessibilityEvent(event);
4090b0a41d8e26eaf0f1d9d922621494daf40964a9aSvetoslav Ganov            }
4103fb3d7c4e756bd32d5abde0abca9ab52d559bc84Adam Powell        }
411736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        return false;
412736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
413736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
414736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    @Override
41530401328c1026389171d454c934c15875c3f7ff0Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
41630401328c1026389171d454c934c15875c3f7ff0Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
4178a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        event.setClassName(TabWidget.class.getName());
418736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        event.setItemCount(getTabCount());
419736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        event.setCurrentItemIndex(mSelectedTab);
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4213730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
4228a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
4238a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
4248a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
4258a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        // this class fires events only when tabs are focused or selected
4268a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && isFocused()) {
4278a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov            event.recycle();
4288a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov            return;
4298a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        }
4308a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.sendAccessibilityEventUnchecked(event);
4318a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
4328a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
4338a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
4348a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
4358a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityNodeInfo(info);
4368a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        info.setClassName(TabWidget.class.getName());
4378a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
4388a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Sets the current tab and focuses the UI on it.
4413730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * This method makes sure that the focused tab matches the selected
4423730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * tab, normally at {@link #setCurrentTab}.  Normally this would not
4433730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * be an issue if we go through the UI, since the UI is responsible
4443730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * for calling TabWidget.onFocusChanged(), but in the case where we
4453730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * are selecting the tab programmatically, we'll need to make sure
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * focus keeps up.
4473730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
4483730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *  @param index The tab that you want focused (highlighted in orange)
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  and selected (tab brought to the front of the widget)
4503730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  @see #setCurrentTab
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void focusCurrentTab(int index) {
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int oldTab = mSelectedTab;
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // set the tab
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setCurrentTab(index);
4583730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // change the focus if applicable.
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (oldTab != index) {
46153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            getChildTabViewAt(index).requestFocus();
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4643730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setEnabled(boolean enabled) {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.setEnabled(enabled);
4683730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
46911f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey        final int count = getTabCount();
47053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        for (int i = 0; i < count; i++) {
47153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            View child = getChildTabViewAt(i);
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            child.setEnabled(enabled);
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void addView(View child) {
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (child.getLayoutParams() == null) {
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final LinearLayout.LayoutParams lp = new LayoutParams(
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    0,
481980a938c1c9a6a5791a8240e5a1e6638ab28dc77Romain Guy                    ViewGroup.LayoutParams.MATCH_PARENT, 1.0f);
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            lp.setMargins(0, 0, 0, 0);
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            child.setLayoutParams(lp);
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Ensure you can navigate to the tab with the keyboard, and you can touch it
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        child.setFocusable(true);
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        child.setClickable(true);
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.addView(child);
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // TODO: detect this via geometry with a tabwidget listener rather
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // than potentially interfere with the view's listener
49453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        child.setOnClickListener(new TabClickListener(getTabCount() - 1));
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        child.setOnFocusChangeListener(this);
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4985ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    @Override
499cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey    public void removeAllViews() {
500cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey        super.removeAllViews();
501cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey        mSelectedTab = -1;
502cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey    }
503cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Provides a way for {@link TabHost} to be notified that the user clicked on a tab indicator.
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    void setTabSelectionListener(OnTabSelectionChanged listener) {
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSelectionChangedListener = listener;
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
51111f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey    /** {@inheritDoc} */
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onFocusChange(View v, boolean hasFocus) {
513acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert        if (v == this && hasFocus && getTabCount() > 0) {
51453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            getChildTabViewAt(mSelectedTab).requestFocus();
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5173730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (hasFocus) {
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = 0;
52053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            int numTabs = getTabCount();
52153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            while (i < numTabs) {
52253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra                if (getChildTabViewAt(i) == v) {
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    setCurrentTab(i);
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mSelectionChangedListener.onTabSelectionChanged(i, false);
5255ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov                    if (isShown()) {
5265ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov                        // a tab is focused so send an event to announce the tab widget state
5275ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
5285ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov                    }
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // registered with each tab indicator so we can notify tab host
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class TabClickListener implements OnClickListener {
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final int mTabIndex;
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private TabClickListener(int tabIndex) {
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTabIndex = tabIndex;
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onClick(View v) {
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSelectionChangedListener.onTabSelectionChanged(mTabIndex, true);
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Let {@link TabHost} know that the user clicked on a tab indicator.
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static interface OnTabSelectionChanged {
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Informs the TabHost which tab was selected. It also indicates
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * if the tab was clicked/pressed or just focused into.
5573730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar         *
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param tabIndex index of the tab that was selected
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param clicked whether the selection changed due to a touch/click
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * or due to focus entering the tab through navigation. Pass true
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * if it was due to a press/click and false otherwise.
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onTabSelectionChanged(int tabIndex, boolean clicked);
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
566