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