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