ActionBarView.java revision 0458796f1401732b38660794148f4c5e5602f432
1/*
2 * Copyright (C) 2010 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 com.android.internal.widget;
18
19import com.android.internal.R;
20import com.android.internal.view.menu.ActionMenuItem;
21import com.android.internal.view.menu.ActionMenuView;
22import com.android.internal.view.menu.MenuBuilder;
23
24import android.app.ActionBar;
25import android.app.ActionBar.NavigationCallback;
26import android.app.Activity;
27import android.content.Context;
28import android.content.pm.ApplicationInfo;
29import android.content.pm.ComponentInfo;
30import android.content.pm.PackageManager;
31import android.content.pm.PackageManager.NameNotFoundException;
32import android.content.res.TypedArray;
33import android.graphics.drawable.Drawable;
34import android.text.TextUtils.TruncateAt;
35import android.util.AttributeSet;
36import android.util.Log;
37import android.view.ActionMode;
38import android.view.Gravity;
39import android.view.LayoutInflater;
40import android.view.Menu;
41import android.view.View;
42import android.view.ViewGroup;
43import android.view.ViewParent;
44import android.widget.AdapterView;
45import android.widget.HorizontalScrollView;
46import android.widget.ImageView;
47import android.widget.LinearLayout;
48import android.widget.Spinner;
49import android.widget.SpinnerAdapter;
50import android.widget.TextView;
51
52/**
53 * @hide
54 */
55public class ActionBarView extends ViewGroup {
56    private static final String TAG = "ActionBarView";
57
58    /**
59     * Display options applied by default
60     */
61    public static final int DISPLAY_DEFAULT = 0;
62
63    /**
64     * Display options that require re-layout as opposed to a simple invalidate
65     */
66    private static final int DISPLAY_RELAYOUT_MASK =
67            ActionBar.DISPLAY_SHOW_HOME |
68            ActionBar.DISPLAY_USE_LOGO |
69            ActionBar.DISPLAY_HOME_AS_UP |
70            ActionBar.DISPLAY_SHOW_CUSTOM |
71            ActionBar.DISPLAY_SHOW_TITLE;
72
73    private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL;
74
75    private final int mContentHeight;
76
77    private int mNavigationMode;
78    private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP;
79    private CharSequence mTitle;
80    private CharSequence mSubtitle;
81    private Drawable mIcon;
82    private Drawable mLogo;
83    private Drawable mDivider;
84    private Drawable mHomeAsUpIndicator;
85
86    private LinearLayout mHomeLayout;
87    private ImageView mHomeAsUpView;
88    private ImageView mIconView;
89    private ImageView mLogoView;
90    private LinearLayout mTitleLayout;
91    private TextView mTitleView;
92    private TextView mSubtitleView;
93    private Spinner mSpinner;
94    private HorizontalScrollView mTabScrollView;
95    private LinearLayout mTabLayout;
96    private View mCustomNavView;
97
98    private int mTitleStyleRes;
99    private int mSubtitleStyleRes;
100
101    private boolean mShowMenu;
102    private boolean mUserTitle;
103
104    private MenuBuilder mOptionsMenu;
105    private ActionMenuView mMenuView;
106
107    private ActionBarContextView mContextView;
108
109    private ActionMenuItem mLogoNavItem;
110
111    private SpinnerAdapter mSpinnerAdapter;
112    private NavigationCallback mCallback;
113
114    private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
115            new AdapterView.OnItemSelectedListener() {
116        public void onItemSelected(AdapterView parent, View view, int position, long id) {
117            if (mCallback != null) {
118                mCallback.onNavigationItemSelected(position, id);
119            }
120        }
121        public void onNothingSelected(AdapterView parent) {
122            // Do nothing
123        }
124    };
125
126    private OnClickListener mTabClickListener = null;
127
128    public ActionBarView(Context context, AttributeSet attrs) {
129        super(context, attrs);
130
131        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar);
132
133        ApplicationInfo appInfo = context.getApplicationInfo();
134        PackageManager pm = context.getPackageManager();
135        mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
136                ActionBar.NAVIGATION_MODE_STANDARD);
137        mTitle = a.getText(R.styleable.ActionBar_title);
138        mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
139
140        mLogo = a.getDrawable(R.styleable.ActionBar_logo);
141        if (mLogo == null) {
142            if (context instanceof Activity) {
143                try {
144                    mLogo = pm.getActivityLogo(((Activity) context).getComponentName());
145                } catch (NameNotFoundException e) {
146                    Log.e(TAG, "Activity component name not found!", e);
147                }
148            }
149            if (mLogo == null) {
150                mLogo = appInfo.loadLogo(pm);
151            }
152        }
153
154        mIcon = a.getDrawable(R.styleable.ActionBar_icon);
155        if (mIcon == null) {
156            if (context instanceof Activity) {
157                try {
158                    mIcon = pm.getActivityIcon(((Activity) context).getComponentName());
159                } catch (NameNotFoundException e) {
160                    Log.e(TAG, "Activity component name not found!", e);
161                }
162            }
163            if (mIcon == null) {
164                mIcon = appInfo.loadIcon(pm);
165            }
166        }
167
168        mHomeLayout = new LinearLayout(context, null,
169                com.android.internal.R.attr.actionButtonStyle);
170        mHomeLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
171                LayoutParams.MATCH_PARENT));
172
173        mHomeAsUpIndicator = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
174
175        mHomeAsUpView = new ImageView(context);
176        mHomeAsUpView.setImageDrawable(mHomeAsUpIndicator);
177        mHomeAsUpView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
178                LayoutParams.MATCH_PARENT));
179        mHomeLayout.addView(mHomeAsUpView);
180
181        Drawable background = a.getDrawable(R.styleable.ActionBar_background);
182        if (background != null) {
183            setBackgroundDrawable(background);
184        }
185
186        mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
187        mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
188
189        setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT));
190
191        final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
192        if (customNavId != 0) {
193            LayoutInflater inflater = LayoutInflater.from(context);
194            mCustomNavView = (View) inflater.inflate(customNavId, null);
195            mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD;
196            setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM);
197        }
198
199        mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
200
201        mDivider = a.getDrawable(R.styleable.ActionBar_divider);
202
203        a.recycle();
204
205        mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
206        mHomeLayout.setOnClickListener(new OnClickListener() {
207          public void onClick(View v) {
208            Context context = getContext();
209            if (context instanceof Activity) {
210              Activity activity = (Activity) context;
211              activity.onOptionsItemSelected(mLogoNavItem);
212            }
213          }
214        });
215        mHomeLayout.setClickable(true);
216        mHomeLayout.setFocusable(true);
217    }
218
219    @Override
220    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
221        // No starting an action mode for an action bar child! (Where would it go?)
222        return null;
223    }
224
225    public void setCallback(NavigationCallback callback) {
226        mCallback = callback;
227    }
228
229    public void setMenu(Menu menu) {
230        MenuBuilder builder = (MenuBuilder) menu;
231        mOptionsMenu = builder;
232        if (mMenuView != null) {
233            removeView(mMenuView);
234        }
235        final ActionMenuView menuView = (ActionMenuView) builder.getMenuView(
236                MenuBuilder.TYPE_ACTION_BUTTON, null);
237        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
238                LayoutParams.MATCH_PARENT);
239        menuView.setLayoutParams(layoutParams);
240        addView(menuView);
241        mMenuView = menuView;
242    }
243
244    public boolean showOverflowMenu() {
245        if (mMenuView != null) {
246            return mMenuView.showOverflowMenu();
247        }
248        return false;
249    }
250
251    public void postShowOverflowMenu() {
252        post(new Runnable() {
253            public void run() {
254                showOverflowMenu();
255            }
256        });
257    }
258
259    public boolean hideOverflowMenu() {
260        if (mMenuView != null) {
261            return mMenuView.hideOverflowMenu();
262        }
263        return false;
264    }
265
266    public boolean isOverflowMenuShowing() {
267        if (mMenuView != null) {
268            return mMenuView.isOverflowMenuShowing();
269        }
270        return false;
271    }
272
273    public boolean isOverflowReserved() {
274        return mMenuView != null && mMenuView.isOverflowReserved();
275    }
276
277    public void setCustomNavigationView(View view) {
278        final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
279        if (mCustomNavView != null && showCustom) {
280            removeView(mCustomNavView);
281        }
282        mCustomNavView = view;
283        if (mCustomNavView != null && showCustom) {
284            addView(mCustomNavView);
285        }
286    }
287
288    public CharSequence getTitle() {
289        return mTitle;
290    }
291
292    /**
293     * Set the action bar title. This will always replace or override window titles.
294     * @param title Title to set
295     *
296     * @see #setWindowTitle(CharSequence)
297     */
298    public void setTitle(CharSequence title) {
299        mUserTitle = true;
300        setTitleImpl(title);
301    }
302
303    /**
304     * Set the window title. A window title will always be replaced or overridden by a user title.
305     * @param title Title to set
306     *
307     * @see #setTitle(CharSequence)
308     */
309    public void setWindowTitle(CharSequence title) {
310        if (!mUserTitle) {
311            setTitleImpl(title);
312        }
313    }
314
315    private void setTitleImpl(CharSequence title) {
316        mTitle = title;
317        if (mTitleView != null) {
318            mTitleView.setText(title);
319        }
320        if (mLogoNavItem != null) {
321            mLogoNavItem.setTitle(title);
322        }
323    }
324
325    public CharSequence getSubtitle() {
326        return mSubtitle;
327    }
328
329    public void setSubtitle(CharSequence subtitle) {
330        mSubtitle = subtitle;
331        if (mSubtitleView != null) {
332            mSubtitleView.setText(subtitle);
333            mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE);
334        }
335    }
336
337    public void setDisplayOptions(int options) {
338        // TODO Remove this once DISPLAY_HIDE_HOME is removed
339        if ((options & ActionBar.DISPLAY_HIDE_HOME) != 0) {
340            options &= ~(ActionBar.DISPLAY_HIDE_HOME | ActionBar.DISPLAY_SHOW_HOME);
341        }
342        // End TODO
343
344        final int flagsChanged = options ^ mDisplayOptions;
345        mDisplayOptions = options;
346        if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
347            final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE;
348            mHomeLayout.setVisibility(vis);
349
350            if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
351                mHomeAsUpView.setVisibility((options & ActionBar.DISPLAY_HOME_AS_UP) != 0
352                        ? VISIBLE : GONE);
353            }
354
355            if (mLogoView != null && (flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) {
356                final boolean logoVis = (options & ActionBar.DISPLAY_USE_LOGO) != 0;
357                mLogoView.setVisibility(logoVis ? VISIBLE : GONE);
358                mIconView.setVisibility(logoVis ? GONE : VISIBLE);
359            }
360
361            if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
362                if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
363                    initTitle();
364                } else {
365                    removeView(mTitleLayout);
366                }
367            }
368
369            if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
370                if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
371                    addView(mCustomNavView);
372                } else {
373                    removeView(mCustomNavView);
374                }
375            }
376
377            requestLayout();
378        } else {
379            invalidate();
380        }
381    }
382
383    public void setNavigationMode(int mode) {
384        final int oldMode = mNavigationMode;
385        if (mode != oldMode) {
386            switch (oldMode) {
387            case ActionBar.NAVIGATION_MODE_LIST:
388                if (mSpinner != null) {
389                    removeView(mSpinner);
390                }
391                break;
392            case ActionBar.NAVIGATION_MODE_TABS:
393                if (mTabLayout != null) {
394                    removeView(mTabScrollView);
395                }
396            }
397
398            switch (mode) {
399            case ActionBar.NAVIGATION_MODE_LIST:
400                mSpinner = new Spinner(mContext, null,
401                        com.android.internal.R.attr.actionDropDownStyle);
402                mSpinner.setAdapter(mSpinnerAdapter);
403                mSpinner.setOnItemSelectedListener(mNavItemSelectedListener);
404                addView(mSpinner);
405                break;
406            case ActionBar.NAVIGATION_MODE_TABS:
407                ensureTabsExist();
408                addView(mTabScrollView);
409                break;
410            }
411            mNavigationMode = mode;
412            requestLayout();
413        }
414    }
415
416    private void ensureTabsExist() {
417        if (mTabScrollView == null) {
418            mTabScrollView = new HorizontalScrollView(getContext());
419            mTabLayout = new LinearLayout(getContext(), null,
420                    com.android.internal.R.attr.actionBarTabBarStyle);
421            mTabScrollView.addView(mTabLayout);
422        }
423    }
424
425    public void setDropdownAdapter(SpinnerAdapter adapter) {
426        mSpinnerAdapter = adapter;
427        if (mSpinner != null) {
428            mSpinner.setAdapter(adapter);
429        }
430    }
431
432    public SpinnerAdapter getDropdownAdapter() {
433        return mSpinnerAdapter;
434    }
435
436    public void setDropdownSelectedPosition(int position) {
437        mSpinner.setSelection(position);
438    }
439
440    public int getDropdownSelectedPosition() {
441        return mSpinner.getSelectedItemPosition();
442    }
443
444    public View getCustomNavigationView() {
445        return mCustomNavView;
446    }
447
448    public int getNavigationMode() {
449        return mNavigationMode;
450    }
451
452    public int getDisplayOptions() {
453        return mDisplayOptions;
454    }
455
456    private TabView createTabView(ActionBar.Tab tab) {
457        final TabView tabView = new TabView(getContext(), tab);
458        tabView.setFocusable(true);
459
460        if (mTabClickListener == null) {
461            mTabClickListener = new TabClickListener();
462        }
463        tabView.setOnClickListener(mTabClickListener);
464        return tabView;
465    }
466
467    public void addTab(ActionBar.Tab tab, boolean setSelected) {
468        ensureTabsExist();
469        View tabView = createTabView(tab);
470        mTabLayout.addView(tabView);
471        if (setSelected) {
472            tabView.setSelected(true);
473        }
474    }
475
476    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
477        ensureTabsExist();
478        final TabView tabView = createTabView(tab);
479        mTabLayout.addView(tabView, position);
480        if (setSelected) {
481            tabView.setSelected(true);
482        }
483    }
484
485    public void removeTabAt(int position) {
486        if (mTabLayout != null) {
487            mTabLayout.removeViewAt(position);
488        }
489    }
490
491    public void removeAllTabs() {
492        if (mTabLayout != null) {
493            mTabLayout.removeAllViews();
494        }
495    }
496
497    @Override
498    protected LayoutParams generateDefaultLayoutParams() {
499        // Used by custom nav views if they don't supply layout params. Everything else
500        // added to an ActionBarView should have them already.
501        return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY);
502    }
503
504    @Override
505    protected void onFinishInflate() {
506        super.onFinishInflate();
507
508        final Context context = getContext();
509
510        if (mLogo != null) {
511            mLogoView = new ImageView(context);
512            mLogoView.setScaleType(ImageView.ScaleType.CENTER);
513            mLogoView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
514                    LayoutParams.MATCH_PARENT));
515            mLogoView.setImageDrawable(mLogo);
516            mLogoView.setVisibility((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0
517                    ? VISIBLE : GONE);
518            mHomeLayout.addView(mLogoView);
519        }
520
521        if (mIcon != null) {
522            mIconView = new ImageView(context, null,
523                    com.android.internal.R.attr.actionButtonStyle);
524            mIconView.setScaleType(ImageView.ScaleType.CENTER);
525            mIconView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
526                    LayoutParams.MATCH_PARENT));
527            mIconView.setImageDrawable(mIcon);
528            mIconView.setVisibility(
529                    (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null
530                    ? VISIBLE : GONE);
531            mHomeLayout.addView(mIconView);
532        }
533
534        addView(mHomeLayout);
535
536        switch (mNavigationMode) {
537        case ActionBar.NAVIGATION_MODE_STANDARD:
538            if (mLogoView == null) {
539                initTitle();
540            }
541            break;
542
543        case ActionBar.NAVIGATION_MODE_LIST:
544            throw new UnsupportedOperationException(
545                    "Inflating list navigation isn't supported yet!");
546
547        case ActionBar.NAVIGATION_MODE_TABS:
548            throw new UnsupportedOperationException(
549                    "Inflating tab navigation isn't supported yet!");
550        }
551
552        if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
553            final ViewParent parent = mCustomNavView.getParent();
554            if (parent != this) {
555                if (parent instanceof ViewGroup) {
556                    ((ViewGroup) parent).removeView(mCustomNavView);
557                }
558                addView(mCustomNavView);
559            }
560        }
561    }
562
563    private void initTitle() {
564        LayoutInflater inflater = LayoutInflater.from(getContext());
565        mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null);
566        mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
567        mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
568
569        if (mTitleStyleRes != 0) {
570            mTitleView.setTextAppearance(mContext, mTitleStyleRes);
571        }
572        if (mTitle != null) {
573            mTitleView.setText(mTitle);
574        }
575
576        if (mSubtitleStyleRes != 0) {
577            mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
578        }
579        if (mSubtitle != null) {
580            mSubtitleView.setText(mSubtitle);
581            mSubtitleView.setVisibility(VISIBLE);
582        }
583
584        addView(mTitleLayout);
585    }
586
587    public void setTabSelected(int position) {
588        ensureTabsExist();
589        final int tabCount = mTabLayout.getChildCount();
590        for (int i = 0; i < tabCount; i++) {
591            final View child = mTabLayout.getChildAt(i);
592            child.setSelected(i == position);
593        }
594    }
595
596    public void setContextView(ActionBarContextView view) {
597        mContextView = view;
598    }
599
600    @Override
601    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
602        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
603        if (widthMode != MeasureSpec.EXACTLY) {
604            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
605                    "with android:layout_width=\"match_parent\" (or fill_parent)");
606        }
607
608        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
609        if (heightMode != MeasureSpec.AT_MOST) {
610            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
611                    "with android:layout_height=\"wrap_content\"");
612        }
613
614        int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
615
616        int maxHeight = mContentHeight > 0 ?
617                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
618
619        final int verticalPadding = getPaddingTop() + getPaddingBottom();
620        final int paddingLeft = getPaddingLeft();
621        final int paddingRight = getPaddingRight();
622        final int height = maxHeight - verticalPadding;
623        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
624
625        int availableWidth = contentWidth - paddingLeft - paddingRight;
626        int leftOfCenter = availableWidth / 2;
627        int rightOfCenter = leftOfCenter;
628
629        if (mHomeLayout.getVisibility() != GONE) {
630            availableWidth = measureChildView(mHomeLayout, availableWidth, childSpecHeight, 0);
631            leftOfCenter -= mHomeLayout.getMeasuredWidth();
632        }
633
634        if (mMenuView != null) {
635            availableWidth = measureChildView(mMenuView, availableWidth,
636                    childSpecHeight, 0);
637            rightOfCenter -= mMenuView.getMeasuredWidth();
638        }
639
640        if (mTitleLayout != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
641            availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
642            leftOfCenter -= mTitleLayout.getMeasuredWidth();
643        }
644
645        switch (mNavigationMode) {
646        case ActionBar.NAVIGATION_MODE_LIST:
647            if (mSpinner != null) {
648                mSpinner.measure(
649                        MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
650                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
651                final int spinnerWidth = mSpinner.getMeasuredWidth();
652                availableWidth -= spinnerWidth;
653                leftOfCenter -= spinnerWidth;
654            }
655            break;
656        case ActionBar.NAVIGATION_MODE_TABS:
657            if (mTabScrollView != null) {
658                mTabScrollView.measure(
659                        MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
660                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
661                final int tabWidth = mTabScrollView.getMeasuredWidth();
662                availableWidth -= tabWidth;
663                leftOfCenter -= tabWidth;
664            }
665            break;
666        }
667
668        if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
669            final LayoutParams lp = generateLayoutParams(mCustomNavView.getLayoutParams());
670            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
671                    (ActionBar.LayoutParams) lp : null;
672
673            int horizontalMargin = 0;
674            int verticalMargin = 0;
675            if (ablp != null) {
676                horizontalMargin = ablp.leftMargin + ablp.rightMargin;
677                verticalMargin = ablp.topMargin + ablp.bottomMargin;
678            }
679
680            // If the action bar is wrapping to its content height, don't allow a custom
681            // view to MATCH_PARENT.
682            int customNavHeightMode;
683            if (mContentHeight <= 0) {
684                customNavHeightMode = MeasureSpec.AT_MOST;
685            } else {
686                customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
687                        MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
688            }
689            final int customNavHeight = Math.max(0,
690                    (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin);
691
692            final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
693                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
694            int customNavWidth = Math.max(0,
695                    (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth)
696                    - horizontalMargin);
697            final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) &
698                    Gravity.HORIZONTAL_GRAVITY_MASK;
699
700            // Centering a custom view is treated specially; we try to center within the whole
701            // action bar rather than in the available space.
702            if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) {
703                customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2;
704            }
705
706            mCustomNavView.measure(
707                    MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode),
708                    MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
709        }
710
711        if (mContentHeight <= 0) {
712            int measuredHeight = 0;
713            final int count = getChildCount();
714            for (int i = 0; i < count; i++) {
715                View v = getChildAt(i);
716                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
717                if (paddedViewHeight > measuredHeight) {
718                    measuredHeight = paddedViewHeight;
719                }
720            }
721            setMeasuredDimension(contentWidth, measuredHeight);
722        } else {
723            setMeasuredDimension(contentWidth, maxHeight);
724        }
725
726        if (mContextView != null) {
727            mContextView.setHeight(getMeasuredHeight());
728        }
729    }
730
731    private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
732        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
733                childSpecHeight);
734
735        availableWidth -= child.getMeasuredWidth();
736        availableWidth -= spacing;
737
738        return availableWidth;
739    }
740
741    @Override
742    protected void onLayout(boolean changed, int l, int t, int r, int b) {
743        int x = getPaddingLeft();
744        final int y = getPaddingTop();
745        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
746
747        if (mHomeLayout.getVisibility() != GONE) {
748            x += positionChild(mHomeLayout, x, y, contentHeight);
749        }
750
751        if (mTitleLayout != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
752            x += positionChild(mTitleLayout, x, y, contentHeight);
753        }
754
755        switch (mNavigationMode) {
756        case ActionBar.NAVIGATION_MODE_STANDARD:
757            break;
758        case ActionBar.NAVIGATION_MODE_LIST:
759            if (mSpinner != null) {
760                x += positionChild(mSpinner, x, y, contentHeight);
761            }
762            break;
763        case ActionBar.NAVIGATION_MODE_TABS:
764            if (mTabScrollView != null) {
765                x += positionChild(mTabScrollView, x, y, contentHeight);
766            }
767        }
768
769        int menuLeft = r - l - getPaddingRight();
770        if (mMenuView != null) {
771            positionChildInverse(mMenuView, menuLeft, y, contentHeight);
772            menuLeft -= mMenuView.getMeasuredWidth();
773        }
774
775        if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
776            LayoutParams lp = mCustomNavView.getLayoutParams();
777            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
778                    (ActionBar.LayoutParams) lp : null;
779
780            final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
781            final int navWidth = mCustomNavView.getMeasuredWidth();
782
783            int topMargin = 0;
784            int bottomMargin = 0;
785            if (ablp != null) {
786                x += ablp.leftMargin;
787                menuLeft -= ablp.rightMargin;
788                topMargin = ablp.topMargin;
789                bottomMargin = ablp.bottomMargin;
790            }
791
792            int hgravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
793            // See if we actually have room to truly center; if not push against left or right.
794            if (hgravity == Gravity.CENTER_HORIZONTAL) {
795                final int centeredLeft = ((mRight - mLeft) - navWidth) / 2;
796                if (centeredLeft < x) {
797                    hgravity = Gravity.LEFT;
798                } else if (centeredLeft + navWidth > menuLeft) {
799                    hgravity = Gravity.RIGHT;
800                }
801            }
802
803            int xpos = 0;
804            switch (hgravity) {
805                case Gravity.CENTER_HORIZONTAL:
806                    xpos = ((mRight - mLeft) - navWidth) / 2;
807                    break;
808                case Gravity.LEFT:
809                    xpos = x;
810                    break;
811                case Gravity.RIGHT:
812                    xpos = menuLeft - navWidth;
813                    break;
814            }
815
816            int ypos = 0;
817            switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
818                case Gravity.CENTER_VERTICAL:
819                    final int paddedTop = mTop + getPaddingTop();
820                    final int paddedBottom = mBottom - getPaddingBottom();
821                    ypos = ((paddedBottom - paddedTop) - mCustomNavView.getMeasuredHeight()) / 2;
822                    break;
823                case Gravity.TOP:
824                    ypos = getPaddingTop() + topMargin;
825                    break;
826                case Gravity.BOTTOM:
827                    ypos = getHeight() - getPaddingBottom() - mCustomNavView.getMeasuredHeight()
828                            - bottomMargin;
829                    break;
830            }
831            x += positionChild(mCustomNavView, xpos, ypos, contentHeight);
832        }
833    }
834
835    private int positionChild(View child, int x, int y, int contentHeight) {
836        int childWidth = child.getMeasuredWidth();
837        int childHeight = child.getMeasuredHeight();
838        int childTop = y + (contentHeight - childHeight) / 2;
839
840        child.layout(x, childTop, x + childWidth, childTop + childHeight);
841
842        return childWidth;
843    }
844
845    private int positionChildInverse(View child, int x, int y, int contentHeight) {
846        int childWidth = child.getMeasuredWidth();
847        int childHeight = child.getMeasuredHeight();
848        int childTop = y + (contentHeight - childHeight) / 2;
849
850        child.layout(x - childWidth, childTop, x, childTop + childHeight);
851
852        return childWidth;
853    }
854
855    private static class TabView extends LinearLayout {
856        private ActionBar.Tab mTab;
857
858        public TabView(Context context, ActionBar.Tab tab) {
859            super(context, null, com.android.internal.R.attr.actionBarTabStyle);
860            mTab = tab;
861
862            final View custom = tab.getCustomView();
863            if (custom != null) {
864                addView(custom);
865            } else {
866                // TODO Style tabs based on the theme
867
868                final Drawable icon = tab.getIcon();
869                final CharSequence text = tab.getText();
870
871                if (icon != null) {
872                    ImageView iconView = new ImageView(context);
873                    iconView.setImageDrawable(icon);
874                    LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
875                            LayoutParams.WRAP_CONTENT);
876                    lp.gravity = Gravity.CENTER_VERTICAL;
877                    iconView.setLayoutParams(lp);
878                    addView(iconView);
879                }
880
881                if (text != null) {
882                    TextView textView = new TextView(context, null,
883                            com.android.internal.R.attr.actionBarTabTextStyle);
884                    textView.setText(text);
885                    textView.setSingleLine();
886                    textView.setEllipsize(TruncateAt.END);
887                    LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
888                            LayoutParams.WRAP_CONTENT);
889                    lp.gravity = Gravity.CENTER_VERTICAL;
890                    textView.setLayoutParams(lp);
891                    addView(textView);
892                }
893            }
894
895            setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
896                    LayoutParams.MATCH_PARENT, 1));
897        }
898
899        public ActionBar.Tab getTab() {
900            return mTab;
901        }
902    }
903
904    private class TabClickListener implements OnClickListener {
905        public void onClick(View view) {
906            TabView tabView = (TabView) view;
907            tabView.getTab().select();
908            final int tabCount = mTabLayout.getChildCount();
909            for (int i = 0; i < tabCount; i++) {
910                final View child = mTabLayout.getChildAt(i);
911                child.setSelected(child == view);
912            }
913        }
914    }
915}
916