ActionBarView.java revision 8c16aa9fa225c277c3ead605b16dc697709bda27
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.ActionMenuPresenter;
22import com.android.internal.view.menu.ActionMenuView;
23import com.android.internal.view.menu.MenuBuilder;
24import com.android.internal.view.menu.MenuItemImpl;
25import com.android.internal.view.menu.MenuPresenter;
26import com.android.internal.view.menu.MenuView;
27import com.android.internal.view.menu.SubMenuBuilder;
28
29import android.animation.LayoutTransition;
30import android.app.ActionBar;
31import android.app.ActionBar.OnNavigationListener;
32import android.app.Activity;
33import android.content.Context;
34import android.content.pm.ApplicationInfo;
35import android.content.pm.PackageManager;
36import android.content.pm.PackageManager.NameNotFoundException;
37import android.content.res.Configuration;
38import android.content.res.TypedArray;
39import android.graphics.drawable.Drawable;
40import android.os.Parcel;
41import android.os.Parcelable;
42import android.text.Layout;
43import android.text.TextUtils;
44import android.util.AttributeSet;
45import android.util.Log;
46import android.view.CollapsibleActionView;
47import android.view.Gravity;
48import android.view.LayoutInflater;
49import android.view.Menu;
50import android.view.MenuItem;
51import android.view.MotionEvent;
52import android.view.View;
53import android.view.ViewGroup;
54import android.view.ViewParent;
55import android.view.Window;
56import android.view.accessibility.AccessibilityEvent;
57import android.widget.AdapterView;
58import android.widget.FrameLayout;
59import android.widget.ImageView;
60import android.widget.LinearLayout;
61import android.widget.ProgressBar;
62import android.widget.Spinner;
63import android.widget.SpinnerAdapter;
64import android.widget.TextView;
65
66/**
67 * @hide
68 */
69public class ActionBarView extends AbsActionBarView {
70    private static final String TAG = "ActionBarView";
71
72    /**
73     * Display options applied by default
74     */
75    public static final int DISPLAY_DEFAULT = 0;
76
77    /**
78     * Display options that require re-layout as opposed to a simple invalidate
79     */
80    private static final int DISPLAY_RELAYOUT_MASK =
81            ActionBar.DISPLAY_SHOW_HOME |
82            ActionBar.DISPLAY_USE_LOGO |
83            ActionBar.DISPLAY_HOME_AS_UP |
84            ActionBar.DISPLAY_SHOW_CUSTOM |
85            ActionBar.DISPLAY_SHOW_TITLE |
86            ActionBar.DISPLAY_TITLE_MULTIPLE_LINES;
87
88    private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.START | Gravity.CENTER_VERTICAL;
89
90    private int mNavigationMode;
91    private int mDisplayOptions = -1;
92    private CharSequence mTitle;
93    private CharSequence mSubtitle;
94    private Drawable mIcon;
95    private Drawable mLogo;
96
97    private HomeView mHomeLayout;
98    private HomeView mExpandedHomeLayout;
99    private LinearLayout mTitleLayout;
100    private TextView mTitleView;
101    private TextView mSubtitleView;
102    private View mTitleUpView;
103    private ViewGroup mUpGoerFive;
104
105    private Spinner mSpinner;
106    private LinearLayout mListNavLayout;
107    private ScrollingTabContainerView mTabScrollView;
108    private View mCustomNavView;
109    private ProgressBar mProgressView;
110    private ProgressBar mIndeterminateProgressView;
111
112    private int mProgressBarPadding;
113    private int mItemPadding;
114
115    private int mTitleStyleRes;
116    private int mSubtitleStyleRes;
117    private int mProgressStyle;
118    private int mIndeterminateProgressStyle;
119
120    private boolean mUserTitle;
121    private boolean mIncludeTabs;
122    private boolean mIsCollapsable;
123    private boolean mIsCollapsed;
124    private boolean mWasHomeEnabled; // Was it enabled before action view expansion?
125
126    private MenuBuilder mOptionsMenu;
127    private boolean mMenuPrepared;
128
129    private ActionBarContextView mContextView;
130
131    private ActionMenuItem mLogoNavItem;
132
133    private SpinnerAdapter mSpinnerAdapter;
134    private OnNavigationListener mCallback;
135
136    private Runnable mTabSelector;
137
138    private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
139    View mExpandedActionView;
140
141    Window.Callback mWindowCallback;
142
143    private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
144            new AdapterView.OnItemSelectedListener() {
145        public void onItemSelected(AdapterView parent, View view, int position, long id) {
146            if (mCallback != null) {
147                mCallback.onNavigationItemSelected(position, id);
148            }
149        }
150        public void onNothingSelected(AdapterView parent) {
151            // Do nothing
152        }
153    };
154
155    private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() {
156        @Override
157        public void onClick(View v) {
158            final MenuItemImpl item = mExpandedMenuPresenter.mCurrentExpandedItem;
159            if (item != null) {
160                item.collapseActionView();
161            }
162        }
163    };
164
165    private final OnClickListener mUpClickListener = new OnClickListener() {
166        public void onClick(View v) {
167            if (mMenuPrepared) {
168                // Only invoke the window callback if the options menu has been initialized.
169                mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
170            }
171        }
172    };
173
174    public ActionBarView(Context context, AttributeSet attrs) {
175        super(context, attrs);
176
177        // Background is always provided by the container.
178        setBackgroundResource(0);
179
180        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar,
181                com.android.internal.R.attr.actionBarStyle, 0);
182
183        ApplicationInfo appInfo = context.getApplicationInfo();
184        PackageManager pm = context.getPackageManager();
185        mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
186                ActionBar.NAVIGATION_MODE_STANDARD);
187        mTitle = a.getText(R.styleable.ActionBar_title);
188        mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
189
190        mLogo = a.getDrawable(R.styleable.ActionBar_logo);
191        if (mLogo == null) {
192            if (context instanceof Activity) {
193                try {
194                    mLogo = pm.getActivityLogo(((Activity) context).getComponentName());
195                } catch (NameNotFoundException e) {
196                    Log.e(TAG, "Activity component name not found!", e);
197                }
198            }
199            if (mLogo == null) {
200                mLogo = appInfo.loadLogo(pm);
201            }
202        }
203
204        mIcon = a.getDrawable(R.styleable.ActionBar_icon);
205        if (mIcon == null) {
206            if (context instanceof Activity) {
207                try {
208                    mIcon = pm.getActivityIcon(((Activity) context).getComponentName());
209                } catch (NameNotFoundException e) {
210                    Log.e(TAG, "Activity component name not found!", e);
211                }
212            }
213            if (mIcon == null) {
214                mIcon = appInfo.loadIcon(pm);
215            }
216        }
217
218        final LayoutInflater inflater = LayoutInflater.from(context);
219
220        final int homeResId = a.getResourceId(
221                com.android.internal.R.styleable.ActionBar_homeLayout,
222                com.android.internal.R.layout.action_bar_home);
223
224        mUpGoerFive = (ViewGroup) inflater.inflate(
225                com.android.internal.R.layout.action_bar_up_container, this, false);
226        mHomeLayout = (HomeView) inflater.inflate(homeResId, mUpGoerFive, false);
227
228        mExpandedHomeLayout = (HomeView) inflater.inflate(homeResId, mUpGoerFive, false);
229        mExpandedHomeLayout.setUp(true);
230        mExpandedHomeLayout.setOnClickListener(mExpandedActionViewUpListener);
231        mExpandedHomeLayout.setContentDescription(getResources().getText(
232                R.string.action_bar_up_description));
233
234        // This needs to highlight/be focusable on its own.
235        // TODO: Clean up the handoff between expanded/normal.
236        final Drawable upBackground = mUpGoerFive.getBackground();
237        if (upBackground != null) {
238            mExpandedHomeLayout.setBackground(upBackground.getConstantState().newDrawable());
239        }
240        mExpandedHomeLayout.setEnabled(true);
241        mExpandedHomeLayout.setFocusable(true);
242
243        mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
244        mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
245        mProgressStyle = a.getResourceId(R.styleable.ActionBar_progressBarStyle, 0);
246        mIndeterminateProgressStyle = a.getResourceId(
247                R.styleable.ActionBar_indeterminateProgressStyle, 0);
248
249        mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0);
250        mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0);
251
252        setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT));
253
254        final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
255        if (customNavId != 0) {
256            mCustomNavView = (View) inflater.inflate(customNavId, this, false);
257            mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD;
258            setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM);
259        }
260
261        mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
262
263        a.recycle();
264
265        mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
266
267        mUpGoerFive.setOnClickListener(mUpClickListener);
268        mUpGoerFive.setClickable(true);
269        mUpGoerFive.setFocusable(true);
270
271        if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
272            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
273        }
274    }
275
276    @Override
277    protected void onConfigurationChanged(Configuration newConfig) {
278        super.onConfigurationChanged(newConfig);
279
280        mTitleView = null;
281        mSubtitleView = null;
282        mTitleUpView = null;
283        if (mTitleLayout != null && mTitleLayout.getParent() == mUpGoerFive) {
284            mUpGoerFive.removeView(mTitleLayout);
285        }
286        mTitleLayout = null;
287        if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
288            initTitle();
289        }
290
291        if (mTabScrollView != null && mIncludeTabs) {
292            ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
293            if (lp != null) {
294                lp.width = LayoutParams.WRAP_CONTENT;
295                lp.height = LayoutParams.MATCH_PARENT;
296            }
297            mTabScrollView.setAllowCollapse(true);
298        }
299    }
300
301    /**
302     * Set the window callback used to invoke menu items; used for dispatching home button presses.
303     * @param cb Window callback to dispatch to
304     */
305    public void setWindowCallback(Window.Callback cb) {
306        mWindowCallback = cb;
307    }
308
309    @Override
310    public void onDetachedFromWindow() {
311        super.onDetachedFromWindow();
312        removeCallbacks(mTabSelector);
313        if (mActionMenuPresenter != null) {
314            mActionMenuPresenter.hideOverflowMenu();
315            mActionMenuPresenter.hideSubMenus();
316        }
317    }
318
319    @Override
320    public boolean shouldDelayChildPressedState() {
321        return false;
322    }
323
324    public void initProgress() {
325        mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle);
326        mProgressView.setId(R.id.progress_horizontal);
327        mProgressView.setMax(10000);
328        mProgressView.setVisibility(GONE);
329        addView(mProgressView);
330    }
331
332    public void initIndeterminateProgress() {
333        mIndeterminateProgressView = new ProgressBar(mContext, null, 0,
334                mIndeterminateProgressStyle);
335        mIndeterminateProgressView.setId(R.id.progress_circular);
336        mIndeterminateProgressView.setVisibility(GONE);
337        addView(mIndeterminateProgressView);
338    }
339
340    @Override
341    public void setSplitActionBar(boolean splitActionBar) {
342        if (mSplitActionBar != splitActionBar) {
343            if (mMenuView != null) {
344                final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
345                if (oldParent != null) {
346                    oldParent.removeView(mMenuView);
347                }
348                if (splitActionBar) {
349                    if (mSplitView != null) {
350                        mSplitView.addView(mMenuView);
351                    }
352                    mMenuView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
353                } else {
354                    addView(mMenuView);
355                    mMenuView.getLayoutParams().width = LayoutParams.WRAP_CONTENT;
356                }
357                mMenuView.requestLayout();
358            }
359            if (mSplitView != null) {
360                mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE);
361            }
362
363            if (mActionMenuPresenter != null) {
364                if (!splitActionBar) {
365                    mActionMenuPresenter.setExpandedActionViewsExclusive(
366                            getResources().getBoolean(
367                                    com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));
368                } else {
369                    mActionMenuPresenter.setExpandedActionViewsExclusive(false);
370                    // Allow full screen width in split mode.
371                    mActionMenuPresenter.setWidthLimit(
372                            getContext().getResources().getDisplayMetrics().widthPixels, true);
373                    // No limit to the item count; use whatever will fit.
374                    mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
375                }
376            }
377            super.setSplitActionBar(splitActionBar);
378        }
379    }
380
381    public boolean isSplitActionBar() {
382        return mSplitActionBar;
383    }
384
385    public boolean hasEmbeddedTabs() {
386        return mIncludeTabs;
387    }
388
389    public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
390        if (mTabScrollView != null) {
391            removeView(mTabScrollView);
392        }
393        mTabScrollView = tabs;
394        mIncludeTabs = tabs != null;
395        if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
396            addView(mTabScrollView);
397            ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
398            lp.width = LayoutParams.WRAP_CONTENT;
399            lp.height = LayoutParams.MATCH_PARENT;
400            tabs.setAllowCollapse(true);
401        }
402    }
403
404    public void setCallback(OnNavigationListener callback) {
405        mCallback = callback;
406    }
407
408    public void setMenuPrepared() {
409        mMenuPrepared = true;
410    }
411
412    public void setMenu(Menu menu, MenuPresenter.Callback cb) {
413        if (menu == mOptionsMenu) return;
414
415        if (mOptionsMenu != null) {
416            mOptionsMenu.removeMenuPresenter(mActionMenuPresenter);
417            mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter);
418        }
419
420        MenuBuilder builder = (MenuBuilder) menu;
421        mOptionsMenu = builder;
422        if (mMenuView != null) {
423            final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
424            if (oldParent != null) {
425                oldParent.removeView(mMenuView);
426            }
427        }
428        if (mActionMenuPresenter == null) {
429            mActionMenuPresenter = new ActionMenuPresenter(mContext);
430            mActionMenuPresenter.setCallback(cb);
431            mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
432            mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
433        }
434
435        ActionMenuView menuView;
436        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
437                LayoutParams.MATCH_PARENT);
438        if (!mSplitActionBar) {
439            mActionMenuPresenter.setExpandedActionViewsExclusive(
440                    getResources().getBoolean(
441                    com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));
442            configPresenters(builder);
443            menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
444            final ViewGroup oldParent = (ViewGroup) menuView.getParent();
445            if (oldParent != null && oldParent != this) {
446                oldParent.removeView(menuView);
447            }
448            addView(menuView, layoutParams);
449        } else {
450            mActionMenuPresenter.setExpandedActionViewsExclusive(false);
451            // Allow full screen width in split mode.
452            mActionMenuPresenter.setWidthLimit(
453                    getContext().getResources().getDisplayMetrics().widthPixels, true);
454            // No limit to the item count; use whatever will fit.
455            mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
456            // Span the whole width
457            layoutParams.width = LayoutParams.MATCH_PARENT;
458            configPresenters(builder);
459            menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
460            if (mSplitView != null) {
461                final ViewGroup oldParent = (ViewGroup) menuView.getParent();
462                if (oldParent != null && oldParent != mSplitView) {
463                    oldParent.removeView(menuView);
464                }
465                menuView.setVisibility(getAnimatedVisibility());
466                mSplitView.addView(menuView, layoutParams);
467            } else {
468                // We'll add this later if we missed it this time.
469                menuView.setLayoutParams(layoutParams);
470            }
471        }
472        mMenuView = menuView;
473    }
474
475    private void configPresenters(MenuBuilder builder) {
476        if (builder != null) {
477            builder.addMenuPresenter(mActionMenuPresenter);
478            builder.addMenuPresenter(mExpandedMenuPresenter);
479        } else {
480            mActionMenuPresenter.initForMenu(mContext, null);
481            mExpandedMenuPresenter.initForMenu(mContext, null);
482            mActionMenuPresenter.updateMenuView(true);
483            mExpandedMenuPresenter.updateMenuView(true);
484        }
485    }
486
487    public boolean hasExpandedActionView() {
488        return mExpandedMenuPresenter != null &&
489                mExpandedMenuPresenter.mCurrentExpandedItem != null;
490    }
491
492    public void collapseActionView() {
493        final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
494                mExpandedMenuPresenter.mCurrentExpandedItem;
495        if (item != null) {
496            item.collapseActionView();
497        }
498    }
499
500    public void setCustomNavigationView(View view) {
501        final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
502        if (mCustomNavView != null && showCustom) {
503            removeView(mCustomNavView);
504        }
505        mCustomNavView = view;
506        if (mCustomNavView != null && showCustom) {
507            addView(mCustomNavView);
508        }
509    }
510
511    public CharSequence getTitle() {
512        return mTitle;
513    }
514
515    /**
516     * Set the action bar title. This will always replace or override window titles.
517     * @param title Title to set
518     *
519     * @see #setWindowTitle(CharSequence)
520     */
521    public void setTitle(CharSequence title) {
522        mUserTitle = true;
523        setTitleImpl(title);
524    }
525
526    /**
527     * Set the window title. A window title will always be replaced or overridden by a user title.
528     * @param title Title to set
529     *
530     * @see #setTitle(CharSequence)
531     */
532    public void setWindowTitle(CharSequence title) {
533        if (!mUserTitle) {
534            setTitleImpl(title);
535        }
536    }
537
538    private void setTitleImpl(CharSequence title) {
539        mTitle = title;
540        if (mTitleView != null) {
541            mTitleView.setText(title);
542            final boolean visible = mExpandedActionView == null &&
543                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
544                    (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
545            mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
546        }
547        if (mLogoNavItem != null) {
548            mLogoNavItem.setTitle(title);
549        }
550    }
551
552    public CharSequence getSubtitle() {
553        return mSubtitle;
554    }
555
556    public void setSubtitle(CharSequence subtitle) {
557        mSubtitle = subtitle;
558        if (mSubtitleView != null) {
559            mSubtitleView.setText(subtitle);
560            mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE);
561            final boolean visible = mExpandedActionView == null &&
562                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
563                    (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
564            mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
565        }
566    }
567
568    public void setHomeButtonEnabled(boolean enable) {
569        setHomeButtonEnabled(enable, true);
570    }
571
572    private void setHomeButtonEnabled(boolean enable, boolean recordState) {
573        if (recordState) {
574            mWasHomeEnabled = enable;
575        }
576
577        if (mExpandedActionView != null) {
578            // There's an action view currently showing and we want to keep the state
579            // configured for the action view at the moment. If we needed to record the
580            // new state for later we will have done so above.
581            return;
582        }
583
584        mUpGoerFive.setEnabled(enable);
585        mUpGoerFive.setFocusable(enable);
586        // Make sure the home button has an accurate content description for accessibility.
587        if (!enable) {
588            mUpGoerFive.setContentDescription(null);
589            mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
590        } else {
591            mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
592            if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
593                mUpGoerFive.setContentDescription(mContext.getResources().getText(
594                        R.string.action_bar_up_description));
595            } else {
596                mUpGoerFive.setContentDescription(mContext.getResources().getText(
597                        R.string.action_bar_home_description));
598            }
599        }
600    }
601
602    public void setDisplayOptions(int options) {
603        final int flagsChanged = mDisplayOptions == -1 ? -1 : options ^ mDisplayOptions;
604        mDisplayOptions = options;
605
606        if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
607            final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0;
608            final int vis = showHome && mExpandedActionView == null ? VISIBLE : GONE;
609            mHomeLayout.setVisibility(vis);
610
611            if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
612                final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0;
613                mHomeLayout.setUp(setUp);
614
615                // Showing home as up implicitly enables interaction with it.
616                // In honeycomb it was always enabled, so make this transition
617                // a bit easier for developers in the common case.
618                // (It would be silly to show it as up without responding to it.)
619                if (setUp) {
620                    setHomeButtonEnabled(true);
621                }
622            }
623
624            if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) {
625                final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0;
626                mHomeLayout.setIcon(logoVis ? mLogo : mIcon);
627            }
628
629            if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
630                if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
631                    initTitle();
632                } else {
633                    mUpGoerFive.removeView(mTitleLayout);
634                }
635            }
636
637            if (mTitleLayout != null && (flagsChanged &
638                    (ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME)) != 0) {
639                final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
640                mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE);
641            }
642
643            if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
644                if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
645                    addView(mCustomNavView);
646                } else {
647                    removeView(mCustomNavView);
648                }
649            }
650
651            if (mTitleLayout != null &&
652                    (flagsChanged & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) {
653                if ((options & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) {
654                    mTitleView.setSingleLine(false);
655                    mTitleView.setMaxLines(2);
656                } else {
657                    mTitleView.setMaxLines(1);
658                    mTitleView.setSingleLine(true);
659                }
660            }
661
662            requestLayout();
663        } else {
664            invalidate();
665        }
666
667        // Make sure the home button has an accurate content description for accessibility.
668        if (!mHomeLayout.isEnabled()) {
669            mHomeLayout.setContentDescription(null);
670            mHomeLayout.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
671        } else {
672            mHomeLayout.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
673            if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
674                mHomeLayout.setContentDescription(mContext.getResources().getText(
675                        R.string.action_bar_up_description));
676            } else {
677                mHomeLayout.setContentDescription(mContext.getResources().getText(
678                        R.string.action_bar_home_description));
679            }
680        }
681    }
682
683    public void setIcon(Drawable icon) {
684        mIcon = icon;
685        if (icon != null &&
686                ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) {
687            mHomeLayout.setIcon(icon);
688        }
689        if (mExpandedActionView != null) {
690            mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources()));
691        }
692    }
693
694    public void setIcon(int resId) {
695        setIcon(mContext.getResources().getDrawable(resId));
696    }
697
698    public void setLogo(Drawable logo) {
699        mLogo = logo;
700        if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
701            mHomeLayout.setIcon(logo);
702        }
703    }
704
705    public void setLogo(int resId) {
706        setLogo(mContext.getResources().getDrawable(resId));
707    }
708
709    public void setNavigationMode(int mode) {
710        final int oldMode = mNavigationMode;
711        if (mode != oldMode) {
712            switch (oldMode) {
713            case ActionBar.NAVIGATION_MODE_LIST:
714                if (mListNavLayout != null) {
715                    removeView(mListNavLayout);
716                }
717                break;
718            case ActionBar.NAVIGATION_MODE_TABS:
719                if (mTabScrollView != null && mIncludeTabs) {
720                    removeView(mTabScrollView);
721                }
722            }
723
724            switch (mode) {
725            case ActionBar.NAVIGATION_MODE_LIST:
726                if (mSpinner == null) {
727                    mSpinner = new Spinner(mContext, null,
728                            com.android.internal.R.attr.actionDropDownStyle);
729                    mSpinner.setId(com.android.internal.R.id.action_bar_spinner);
730                    mListNavLayout = new LinearLayout(mContext, null,
731                            com.android.internal.R.attr.actionBarTabBarStyle);
732                    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
733                            LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
734                    params.gravity = Gravity.CENTER;
735                    mListNavLayout.addView(mSpinner, params);
736                }
737                if (mSpinner.getAdapter() != mSpinnerAdapter) {
738                    mSpinner.setAdapter(mSpinnerAdapter);
739                }
740                mSpinner.setOnItemSelectedListener(mNavItemSelectedListener);
741                addView(mListNavLayout);
742                break;
743            case ActionBar.NAVIGATION_MODE_TABS:
744                if (mTabScrollView != null && mIncludeTabs) {
745                    addView(mTabScrollView);
746                }
747                break;
748            }
749            mNavigationMode = mode;
750            requestLayout();
751        }
752    }
753
754    public void setDropdownAdapter(SpinnerAdapter adapter) {
755        mSpinnerAdapter = adapter;
756        if (mSpinner != null) {
757            mSpinner.setAdapter(adapter);
758        }
759    }
760
761    public SpinnerAdapter getDropdownAdapter() {
762        return mSpinnerAdapter;
763    }
764
765    public void setDropdownSelectedPosition(int position) {
766        mSpinner.setSelection(position);
767    }
768
769    public int getDropdownSelectedPosition() {
770        return mSpinner.getSelectedItemPosition();
771    }
772
773    public View getCustomNavigationView() {
774        return mCustomNavView;
775    }
776
777    public int getNavigationMode() {
778        return mNavigationMode;
779    }
780
781    public int getDisplayOptions() {
782        return mDisplayOptions;
783    }
784
785    @Override
786    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
787        // Used by custom nav views if they don't supply layout params. Everything else
788        // added to an ActionBarView should have them already.
789        return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY);
790    }
791
792    @Override
793    protected void onFinishInflate() {
794        super.onFinishInflate();
795
796        mUpGoerFive.addView(mHomeLayout, 0);
797        addView(mUpGoerFive);
798
799        if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
800            final ViewParent parent = mCustomNavView.getParent();
801            if (parent != this) {
802                if (parent instanceof ViewGroup) {
803                    ((ViewGroup) parent).removeView(mCustomNavView);
804                }
805                addView(mCustomNavView);
806            }
807        }
808    }
809
810    private void initTitle() {
811        if (mTitleLayout == null) {
812            LayoutInflater inflater = LayoutInflater.from(getContext());
813            mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item,
814                    this, false);
815            mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
816            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
817            mTitleUpView = (View) mTitleLayout.findViewById(R.id.up);
818
819            if (mTitleStyleRes != 0) {
820                mTitleView.setTextAppearance(mContext, mTitleStyleRes);
821            }
822            if (mTitle != null) {
823                mTitleView.setText(mTitle);
824            }
825
826            if (mSubtitleStyleRes != 0) {
827                mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
828            }
829            if (mSubtitle != null) {
830                mSubtitleView.setText(mSubtitle);
831                mSubtitleView.setVisibility(VISIBLE);
832            }
833
834            final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
835            final boolean showHome = (mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0;
836            final boolean showTitleUp = !showHome;
837            mTitleUpView.setVisibility(showTitleUp ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE);
838        }
839
840        mUpGoerFive.addView(mTitleLayout);
841        if (mExpandedActionView != null ||
842                (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) {
843            // Don't show while in expanded mode or with empty text
844            mTitleLayout.setVisibility(GONE);
845        } else {
846            mTitleLayout.setVisibility(VISIBLE);
847        }
848    }
849
850    public void setContextView(ActionBarContextView view) {
851        mContextView = view;
852    }
853
854    public void setCollapsable(boolean collapsable) {
855        mIsCollapsable = collapsable;
856    }
857
858    public boolean isCollapsed() {
859        return mIsCollapsed;
860    }
861
862    /**
863     * @return True if any characters in the title were truncated
864     */
865    public boolean isTitleTruncated() {
866        if (mTitleView == null) {
867            return false;
868        }
869
870        final Layout titleLayout = mTitleView.getLayout();
871        if (titleLayout == null) {
872            return false;
873        }
874
875        final int lineCount = titleLayout.getLineCount();
876        for (int i = 0; i < lineCount; i++) {
877            if (titleLayout.getEllipsisCount(i) > 0) {
878                return true;
879            }
880        }
881        return false;
882    }
883
884    @Override
885    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
886        final int childCount = getChildCount();
887        if (mIsCollapsable) {
888            int visibleChildren = 0;
889            for (int i = 0; i < childCount; i++) {
890                final View child = getChildAt(i);
891                if (child.getVisibility() != GONE &&
892                        !(child == mMenuView && mMenuView.getChildCount() == 0) &&
893                        child != mUpGoerFive) {
894                    visibleChildren++;
895                }
896            }
897
898            final int upChildCount = mUpGoerFive.getChildCount();
899            for (int i = 0; i < upChildCount; i++) {
900                final View child = mUpGoerFive.getChildAt(i);
901                if (child.getVisibility() != GONE) {
902                    visibleChildren++;
903                }
904            }
905
906            if (visibleChildren == 0) {
907                // No size for an empty action bar when collapsable.
908                setMeasuredDimension(0, 0);
909                mIsCollapsed = true;
910                return;
911            }
912        }
913        mIsCollapsed = false;
914
915        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
916        if (widthMode != MeasureSpec.EXACTLY) {
917            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
918                    "with android:layout_width=\"match_parent\" (or fill_parent)");
919        }
920
921        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
922        if (heightMode != MeasureSpec.AT_MOST) {
923            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
924                    "with android:layout_height=\"wrap_content\"");
925        }
926
927        int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
928
929        int maxHeight = mContentHeight >= 0 ?
930                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
931
932        final int verticalPadding = getPaddingTop() + getPaddingBottom();
933        final int paddingLeft = getPaddingLeft();
934        final int paddingRight = getPaddingRight();
935        final int height = maxHeight - verticalPadding;
936        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
937        final int exactHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
938
939        int availableWidth = contentWidth - paddingLeft - paddingRight;
940        int leftOfCenter = availableWidth / 2;
941        int rightOfCenter = leftOfCenter;
942
943        HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
944
945        int homeWidth = 0;
946        if (homeLayout.getVisibility() != GONE && homeLayout.getParent() == mUpGoerFive) {
947            final ViewGroup.LayoutParams lp = homeLayout.getLayoutParams();
948            int homeWidthSpec;
949            if (lp.width < 0) {
950                homeWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
951            } else {
952                homeWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
953            }
954
955            /*
956             * This is a little weird.
957             * We're only measuring the *home* affordance within the Up container here
958             * on purpose, because we want to give the available space to all other views before
959             * the title text. We'll remeasure the whole up container again later.
960             */
961            homeLayout.measure(homeWidthSpec, exactHeightSpec);
962            homeWidth = homeLayout.getMeasuredWidth();
963            final int homeOffsetWidth = homeWidth + homeLayout.getStartOffset();
964            availableWidth = Math.max(0, availableWidth - homeOffsetWidth);
965            leftOfCenter = Math.max(0, availableWidth - homeOffsetWidth);
966        }
967
968        if (mMenuView != null && mMenuView.getParent() == this) {
969            availableWidth = measureChildView(mMenuView, availableWidth, exactHeightSpec, 0);
970            rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth());
971        }
972
973        if (mIndeterminateProgressView != null &&
974                mIndeterminateProgressView.getVisibility() != GONE) {
975            availableWidth = measureChildView(mIndeterminateProgressView, availableWidth,
976                    childSpecHeight, 0);
977            rightOfCenter = Math.max(0,
978                    rightOfCenter - mIndeterminateProgressView.getMeasuredWidth());
979        }
980
981        final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
982                (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
983
984        if (mExpandedActionView == null) {
985            switch (mNavigationMode) {
986                case ActionBar.NAVIGATION_MODE_LIST:
987                    if (mListNavLayout != null) {
988                        final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding;
989                        availableWidth = Math.max(0, availableWidth - itemPaddingSize);
990                        leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize);
991                        mListNavLayout.measure(
992                                MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
993                                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
994                        final int listNavWidth = mListNavLayout.getMeasuredWidth();
995                        availableWidth = Math.max(0, availableWidth - listNavWidth);
996                        leftOfCenter = Math.max(0, leftOfCenter - listNavWidth);
997                    }
998                    break;
999                case ActionBar.NAVIGATION_MODE_TABS:
1000                    if (mTabScrollView != null) {
1001                        final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding;
1002                        availableWidth = Math.max(0, availableWidth - itemPaddingSize);
1003                        leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize);
1004                        mTabScrollView.measure(
1005                                MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
1006                                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
1007                        final int tabWidth = mTabScrollView.getMeasuredWidth();
1008                        availableWidth = Math.max(0, availableWidth - tabWidth);
1009                        leftOfCenter = Math.max(0, leftOfCenter - tabWidth);
1010                    }
1011                    break;
1012            }
1013        }
1014
1015        View customView = null;
1016        if (mExpandedActionView != null) {
1017            customView = mExpandedActionView;
1018        } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 &&
1019                mCustomNavView != null) {
1020            customView = mCustomNavView;
1021        }
1022
1023        if (customView != null) {
1024            final ViewGroup.LayoutParams lp = generateLayoutParams(customView.getLayoutParams());
1025            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
1026                    (ActionBar.LayoutParams) lp : null;
1027
1028            int horizontalMargin = 0;
1029            int verticalMargin = 0;
1030            if (ablp != null) {
1031                horizontalMargin = ablp.leftMargin + ablp.rightMargin;
1032                verticalMargin = ablp.topMargin + ablp.bottomMargin;
1033            }
1034
1035            // If the action bar is wrapping to its content height, don't allow a custom
1036            // view to MATCH_PARENT.
1037            int customNavHeightMode;
1038            if (mContentHeight <= 0) {
1039                customNavHeightMode = MeasureSpec.AT_MOST;
1040            } else {
1041                customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
1042                        MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
1043            }
1044            final int customNavHeight = Math.max(0,
1045                    (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin);
1046
1047            final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
1048                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
1049            int customNavWidth = Math.max(0,
1050                    (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth)
1051                    - horizontalMargin);
1052            final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) &
1053                    Gravity.HORIZONTAL_GRAVITY_MASK;
1054
1055            // Centering a custom view is treated specially; we try to center within the whole
1056            // action bar rather than in the available space.
1057            if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) {
1058                customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2;
1059            }
1060
1061            customView.measure(
1062                    MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode),
1063                    MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
1064            availableWidth -= horizontalMargin + customView.getMeasuredWidth();
1065        }
1066
1067        /*
1068         * Measure the whole up container now, allowing for the full home+title sections.
1069         * (This will re-measure the home view.)
1070         */
1071        availableWidth = measureChildView(mUpGoerFive, availableWidth + homeWidth,
1072                MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY), 0);
1073        if (mTitleLayout != null) {
1074            leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth());
1075        }
1076
1077        if (mContentHeight <= 0) {
1078            int measuredHeight = 0;
1079            for (int i = 0; i < childCount; i++) {
1080                View v = getChildAt(i);
1081                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
1082                if (paddedViewHeight > measuredHeight) {
1083                    measuredHeight = paddedViewHeight;
1084                }
1085            }
1086            setMeasuredDimension(contentWidth, measuredHeight);
1087        } else {
1088            setMeasuredDimension(contentWidth, maxHeight);
1089        }
1090
1091        if (mContextView != null) {
1092            mContextView.setContentHeight(getMeasuredHeight());
1093        }
1094
1095        if (mProgressView != null && mProgressView.getVisibility() != GONE) {
1096            mProgressView.measure(MeasureSpec.makeMeasureSpec(
1097                    contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY),
1098                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST));
1099        }
1100    }
1101
1102    @Override
1103    protected void onLayout(boolean changed, int l, int t, int r, int b) {
1104        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
1105
1106        if (contentHeight <= 0) {
1107            // Nothing to do if we can't see anything.
1108            return;
1109        }
1110
1111        final boolean isLayoutRtl = isLayoutRtl();
1112        final int direction = isLayoutRtl ? +1 : -1;
1113        int menuStart = isLayoutRtl ? getPaddingLeft() : r - l - getPaddingRight();
1114        // In LTR mode, we start from left padding and go to the right; in RTL mode, we start
1115        // from the padding right and go to the left (in reverse way)
1116        int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft();
1117        final int y = getPaddingTop();
1118
1119        HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
1120        final int startOffset = homeLayout.getVisibility() != GONE &&
1121                homeLayout.getParent() == mUpGoerFive ? homeLayout.getStartOffset() : 0;
1122
1123        // Position the up container based on where the edge of the home layout should go.
1124        x += positionChild(mUpGoerFive,
1125                next(x, startOffset, isLayoutRtl), y, contentHeight, isLayoutRtl);
1126        x = next(x, startOffset, isLayoutRtl);
1127
1128        if (mExpandedActionView == null) {
1129            final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
1130                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
1131
1132            switch (mNavigationMode) {
1133                case ActionBar.NAVIGATION_MODE_STANDARD:
1134                    break;
1135                case ActionBar.NAVIGATION_MODE_LIST:
1136                    if (mListNavLayout != null) {
1137                        if (showTitle) {
1138                            x = next(x, mItemPadding, isLayoutRtl);
1139                        }
1140                        x += positionChild(mListNavLayout, x, y, contentHeight, isLayoutRtl);
1141                        x = next(x, mItemPadding, isLayoutRtl);
1142                    }
1143                    break;
1144                case ActionBar.NAVIGATION_MODE_TABS:
1145                    if (mTabScrollView != null) {
1146                        if (showTitle) x = next(x, mItemPadding, isLayoutRtl);
1147                        x += positionChild(mTabScrollView, x, y, contentHeight, isLayoutRtl);
1148                        x = next(x, mItemPadding, isLayoutRtl);
1149                    }
1150                    break;
1151            }
1152        }
1153
1154        if (mMenuView != null && mMenuView.getParent() == this) {
1155            positionChild(mMenuView, menuStart, y, contentHeight, !isLayoutRtl);
1156            menuStart += direction * mMenuView.getMeasuredWidth();
1157        }
1158
1159        if (mIndeterminateProgressView != null &&
1160                mIndeterminateProgressView.getVisibility() != GONE) {
1161            positionChild(mIndeterminateProgressView, menuStart, y, contentHeight, !isLayoutRtl);
1162            menuStart += direction * mIndeterminateProgressView.getMeasuredWidth();
1163        }
1164
1165        View customView = null;
1166        if (mExpandedActionView != null) {
1167            customView = mExpandedActionView;
1168        } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 &&
1169                mCustomNavView != null) {
1170            customView = mCustomNavView;
1171        }
1172        if (customView != null) {
1173            final int layoutDirection = getLayoutDirection();
1174            ViewGroup.LayoutParams lp = customView.getLayoutParams();
1175            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
1176                    (ActionBar.LayoutParams) lp : null;
1177            final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
1178            final int navWidth = customView.getMeasuredWidth();
1179
1180            int topMargin = 0;
1181            int bottomMargin = 0;
1182            if (ablp != null) {
1183                x = next(x, ablp.getMarginStart(), isLayoutRtl);
1184                menuStart += direction * ablp.getMarginEnd();
1185                topMargin = ablp.topMargin;
1186                bottomMargin = ablp.bottomMargin;
1187            }
1188
1189            int hgravity = gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1190            // See if we actually have room to truly center; if not push against left or right.
1191            if (hgravity == Gravity.CENTER_HORIZONTAL) {
1192                final int centeredLeft = ((mRight - mLeft) - navWidth) / 2;
1193                if (isLayoutRtl) {
1194                    final int centeredStart = centeredLeft + navWidth;
1195                    final int centeredEnd = centeredLeft;
1196                    if (centeredStart > x) {
1197                        hgravity = Gravity.RIGHT;
1198                    } else if (centeredEnd < menuStart) {
1199                        hgravity = Gravity.LEFT;
1200                    }
1201                } else {
1202                    final int centeredStart = centeredLeft;
1203                    final int centeredEnd = centeredLeft + navWidth;
1204                    if (centeredStart < x) {
1205                        hgravity = Gravity.LEFT;
1206                    } else if (centeredEnd > menuStart) {
1207                        hgravity = Gravity.RIGHT;
1208                    }
1209                }
1210            } else if (gravity == Gravity.NO_GRAVITY) {
1211                hgravity = Gravity.START;
1212            }
1213
1214            int xpos = 0;
1215            switch (Gravity.getAbsoluteGravity(hgravity, layoutDirection)) {
1216                case Gravity.CENTER_HORIZONTAL:
1217                    xpos = ((mRight - mLeft) - navWidth) / 2;
1218                    break;
1219                case Gravity.LEFT:
1220                    xpos = isLayoutRtl ? menuStart : x;
1221                    break;
1222                case Gravity.RIGHT:
1223                    xpos = isLayoutRtl ? x - navWidth : menuStart - navWidth;
1224                    break;
1225            }
1226
1227            int vgravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
1228
1229            if (gravity == Gravity.NO_GRAVITY) {
1230                vgravity = Gravity.CENTER_VERTICAL;
1231            }
1232
1233            int ypos = 0;
1234            switch (vgravity) {
1235                case Gravity.CENTER_VERTICAL:
1236                    final int paddedTop = getPaddingTop();
1237                    final int paddedBottom = mBottom - mTop - getPaddingBottom();
1238                    ypos = ((paddedBottom - paddedTop) - customView.getMeasuredHeight()) / 2;
1239                    break;
1240                case Gravity.TOP:
1241                    ypos = getPaddingTop() + topMargin;
1242                    break;
1243                case Gravity.BOTTOM:
1244                    ypos = getHeight() - getPaddingBottom() - customView.getMeasuredHeight()
1245                            - bottomMargin;
1246                    break;
1247            }
1248            final int customWidth = customView.getMeasuredWidth();
1249            customView.layout(xpos, ypos, xpos + customWidth,
1250                    ypos + customView.getMeasuredHeight());
1251            x = next(x, customWidth, isLayoutRtl);
1252        }
1253
1254        if (mProgressView != null) {
1255            mProgressView.bringToFront();
1256            final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2;
1257            mProgressView.layout(mProgressBarPadding, -halfProgressHeight,
1258                    mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight);
1259        }
1260    }
1261
1262    @Override
1263    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
1264        return new ActionBar.LayoutParams(getContext(), attrs);
1265    }
1266
1267    @Override
1268    public ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
1269        if (lp == null) {
1270            lp = generateDefaultLayoutParams();
1271        }
1272        return lp;
1273    }
1274
1275    @Override
1276    public Parcelable onSaveInstanceState() {
1277        Parcelable superState = super.onSaveInstanceState();
1278        SavedState state = new SavedState(superState);
1279
1280        if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) {
1281            state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId();
1282        }
1283
1284        state.isOverflowOpen = isOverflowMenuShowing();
1285
1286        return state;
1287    }
1288
1289    @Override
1290    public void onRestoreInstanceState(Parcelable p) {
1291        SavedState state = (SavedState) p;
1292
1293        super.onRestoreInstanceState(state.getSuperState());
1294
1295        if (state.expandedMenuItemId != 0 &&
1296                mExpandedMenuPresenter != null && mOptionsMenu != null) {
1297            final MenuItem item = mOptionsMenu.findItem(state.expandedMenuItemId);
1298            if (item != null) {
1299                item.expandActionView();
1300            }
1301        }
1302
1303        if (state.isOverflowOpen) {
1304            postShowOverflowMenu();
1305        }
1306    }
1307
1308    static class SavedState extends BaseSavedState {
1309        int expandedMenuItemId;
1310        boolean isOverflowOpen;
1311
1312        SavedState(Parcelable superState) {
1313            super(superState);
1314        }
1315
1316        private SavedState(Parcel in) {
1317            super(in);
1318            expandedMenuItemId = in.readInt();
1319            isOverflowOpen = in.readInt() != 0;
1320        }
1321
1322        @Override
1323        public void writeToParcel(Parcel out, int flags) {
1324            super.writeToParcel(out, flags);
1325            out.writeInt(expandedMenuItemId);
1326            out.writeInt(isOverflowOpen ? 1 : 0);
1327        }
1328
1329        public static final Parcelable.Creator<SavedState> CREATOR =
1330                new Parcelable.Creator<SavedState>() {
1331            public SavedState createFromParcel(Parcel in) {
1332                return new SavedState(in);
1333            }
1334
1335            public SavedState[] newArray(int size) {
1336                return new SavedState[size];
1337            }
1338        };
1339    }
1340
1341    private static class HomeView extends FrameLayout {
1342        private View mUpView;
1343        private ImageView mIconView;
1344        private int mUpWidth;
1345
1346        private static final long DEFAULT_TRANSITION_DURATION = 150;
1347
1348        public HomeView(Context context) {
1349            this(context, null);
1350        }
1351
1352        public HomeView(Context context, AttributeSet attrs) {
1353            super(context, attrs);
1354            LayoutTransition t = getLayoutTransition();
1355            if (t != null) {
1356                // Set a lower duration than the default
1357                t.setDuration(DEFAULT_TRANSITION_DURATION);
1358            }
1359        }
1360
1361        public void setUp(boolean isUp) {
1362            mUpView.setVisibility(isUp ? VISIBLE : GONE);
1363        }
1364
1365        public void setIcon(Drawable icon) {
1366            mIconView.setImageDrawable(icon);
1367        }
1368
1369        @Override
1370        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1371            onPopulateAccessibilityEvent(event);
1372            return true;
1373        }
1374
1375        @Override
1376        public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
1377            super.onPopulateAccessibilityEvent(event);
1378            final CharSequence cdesc = getContentDescription();
1379            if (!TextUtils.isEmpty(cdesc)) {
1380                event.getText().add(cdesc);
1381            }
1382        }
1383
1384        @Override
1385        public boolean dispatchHoverEvent(MotionEvent event) {
1386            // Don't allow children to hover; we want this to be treated as a single component.
1387            return onHoverEvent(event);
1388        }
1389
1390        @Override
1391        protected void onFinishInflate() {
1392            mUpView = findViewById(com.android.internal.R.id.up);
1393            mIconView = (ImageView) findViewById(com.android.internal.R.id.home);
1394        }
1395
1396        public int getStartOffset() {
1397            return mUpView.getVisibility() == GONE ? mUpWidth : 0;
1398        }
1399
1400        @Override
1401        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1402            measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
1403            final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
1404            mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
1405            int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth;
1406            int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
1407            measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
1408            final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
1409            width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
1410            height = Math.max(height,
1411                    iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin);
1412
1413            final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
1414            final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
1415            final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
1416            final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
1417
1418            switch (widthMode) {
1419                case MeasureSpec.AT_MOST:
1420                    width = Math.min(width, widthSize);
1421                    break;
1422                case MeasureSpec.EXACTLY:
1423                    width = widthSize;
1424                    break;
1425                case MeasureSpec.UNSPECIFIED:
1426                default:
1427                    break;
1428            }
1429            switch (heightMode) {
1430                case MeasureSpec.AT_MOST:
1431                    height = Math.min(height, heightSize);
1432                    break;
1433                case MeasureSpec.EXACTLY:
1434                    height = heightSize;
1435                    break;
1436                case MeasureSpec.UNSPECIFIED:
1437                default:
1438                    break;
1439            }
1440            setMeasuredDimension(width, height);
1441        }
1442
1443        @Override
1444        protected void onLayout(boolean changed, int l, int t, int r, int b) {
1445            final int vCenter = (b - t) / 2;
1446            final boolean isLayoutRtl = isLayoutRtl();
1447            final int width = getWidth();
1448            int upOffset = 0;
1449            if (mUpView.getVisibility() != GONE) {
1450                final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
1451                final int upHeight = mUpView.getMeasuredHeight();
1452                final int upWidth = mUpView.getMeasuredWidth();
1453                upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
1454                final int upTop = vCenter - upHeight / 2;
1455                final int upBottom = upTop + upHeight;
1456                final int upRight;
1457                final int upLeft;
1458                if (isLayoutRtl) {
1459                    upRight = width;
1460                    upLeft = upRight - upWidth;
1461                    r -= upOffset;
1462                } else {
1463                    upRight = upWidth;
1464                    upLeft = 0;
1465                    l += upOffset;
1466                }
1467                mUpView.layout(upLeft, upTop, upRight, upBottom);
1468            }
1469
1470            final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
1471            final int iconHeight = mIconView.getMeasuredHeight();
1472            final int iconWidth = mIconView.getMeasuredWidth();
1473            final int hCenter = (r - l) / 2;
1474            final int iconTop = Math.max(iconLp.topMargin, vCenter - iconHeight / 2);
1475            final int iconBottom = iconTop + iconHeight;
1476            final int iconLeft;
1477            final int iconRight;
1478            int marginStart = iconLp.getMarginStart();
1479            final int delta = Math.max(marginStart, hCenter - iconWidth / 2);
1480            if (isLayoutRtl) {
1481                iconRight = width - upOffset - delta;
1482                iconLeft = iconRight - iconWidth;
1483            } else {
1484                iconLeft = upOffset + delta;
1485                iconRight = iconLeft + iconWidth;
1486            }
1487            mIconView.layout(iconLeft, iconTop, iconRight, iconBottom);
1488        }
1489    }
1490
1491    private class ExpandedActionViewMenuPresenter implements MenuPresenter {
1492        MenuBuilder mMenu;
1493        MenuItemImpl mCurrentExpandedItem;
1494
1495        @Override
1496        public void initForMenu(Context context, MenuBuilder menu) {
1497            // Clear the expanded action view when menus change.
1498            if (mMenu != null && mCurrentExpandedItem != null) {
1499                mMenu.collapseItemActionView(mCurrentExpandedItem);
1500            }
1501            mMenu = menu;
1502        }
1503
1504        @Override
1505        public MenuView getMenuView(ViewGroup root) {
1506            return null;
1507        }
1508
1509        @Override
1510        public void updateMenuView(boolean cleared) {
1511            // Make sure the expanded item we have is still there.
1512            if (mCurrentExpandedItem != null) {
1513                boolean found = false;
1514
1515                if (mMenu != null) {
1516                    final int count = mMenu.size();
1517                    for (int i = 0; i < count; i++) {
1518                        final MenuItem item = mMenu.getItem(i);
1519                        if (item == mCurrentExpandedItem) {
1520                            found = true;
1521                            break;
1522                        }
1523                    }
1524                }
1525
1526                if (!found) {
1527                    // The item we had expanded disappeared. Collapse.
1528                    collapseItemActionView(mMenu, mCurrentExpandedItem);
1529                }
1530            }
1531        }
1532
1533        @Override
1534        public void setCallback(Callback cb) {
1535        }
1536
1537        @Override
1538        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
1539            return false;
1540        }
1541
1542        @Override
1543        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1544        }
1545
1546        @Override
1547        public boolean flagActionItems() {
1548            return false;
1549        }
1550
1551        @Override
1552        public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
1553            mExpandedActionView = item.getActionView();
1554            mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources()));
1555            mCurrentExpandedItem = item;
1556            if (mExpandedActionView.getParent() != ActionBarView.this) {
1557                addView(mExpandedActionView);
1558            }
1559            if (mExpandedHomeLayout.getParent() != mUpGoerFive) {
1560                mUpGoerFive.addView(mExpandedHomeLayout);
1561            }
1562            mHomeLayout.setVisibility(GONE);
1563            if (mTitleLayout != null) mTitleLayout.setVisibility(GONE);
1564            if (mTabScrollView != null) mTabScrollView.setVisibility(GONE);
1565            if (mSpinner != null) mSpinner.setVisibility(GONE);
1566            if (mCustomNavView != null) mCustomNavView.setVisibility(GONE);
1567            setHomeButtonEnabled(false, false);
1568            requestLayout();
1569            item.setActionViewExpanded(true);
1570
1571            if (mExpandedActionView instanceof CollapsibleActionView) {
1572                ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
1573            }
1574
1575            return true;
1576        }
1577
1578        @Override
1579        public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
1580            // Do this before detaching the actionview from the hierarchy, in case
1581            // it needs to dismiss the soft keyboard, etc.
1582            if (mExpandedActionView instanceof CollapsibleActionView) {
1583                ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
1584            }
1585
1586            removeView(mExpandedActionView);
1587            mUpGoerFive.removeView(mExpandedHomeLayout);
1588            mExpandedActionView = null;
1589            if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) {
1590                mHomeLayout.setVisibility(VISIBLE);
1591            }
1592            if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
1593                if (mTitleLayout == null) {
1594                    initTitle();
1595                } else {
1596                    mTitleLayout.setVisibility(VISIBLE);
1597                }
1598            }
1599            if (mTabScrollView != null) mTabScrollView.setVisibility(VISIBLE);
1600            if (mSpinner != null) mSpinner.setVisibility(VISIBLE);
1601            if (mCustomNavView != null) mCustomNavView.setVisibility(VISIBLE);
1602
1603            mExpandedHomeLayout.setIcon(null);
1604            mCurrentExpandedItem = null;
1605            setHomeButtonEnabled(mWasHomeEnabled); // Set by expandItemActionView above
1606            requestLayout();
1607            item.setActionViewExpanded(false);
1608
1609            return true;
1610        }
1611
1612        @Override
1613        public int getId() {
1614            return 0;
1615        }
1616
1617        @Override
1618        public Parcelable onSaveInstanceState() {
1619            return null;
1620        }
1621
1622        @Override
1623        public void onRestoreInstanceState(Parcelable state) {
1624        }
1625    }
1626}
1627