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