ActionBarView.java revision 8eea3ea5591e59f55cbb4f6b2b7e9363a285ced3
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.ActionMenuPresenter;
45import android.widget.ActionMenuView;
46import android.widget.AdapterView;
47import android.widget.FrameLayout;
48import android.widget.ImageView;
49import android.widget.LinearLayout;
50import android.widget.ProgressBar;
51import android.widget.Spinner;
52import android.widget.SpinnerAdapter;
53import android.widget.TextView;
54import com.android.internal.R;
55import com.android.internal.transition.ActionBarTransition;
56import com.android.internal.view.menu.ActionMenuItem;
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            layoutParams.height = mContentHeight;
434            configPresenters(builder);
435            menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
436            if (mSplitView != null) {
437                final ViewGroup oldParent = (ViewGroup) menuView.getParent();
438                if (oldParent != null && oldParent != mSplitView) {
439                    oldParent.removeView(menuView);
440                }
441                menuView.setVisibility(getAnimatedVisibility());
442                mSplitView.addView(menuView, layoutParams);
443            } else {
444                // We'll add this later if we missed it this time.
445                menuView.setLayoutParams(layoutParams);
446            }
447        }
448        mMenuView = menuView;
449    }
450
451    private void configPresenters(MenuBuilder builder) {
452        if (builder != null) {
453            builder.addMenuPresenter(mActionMenuPresenter);
454            builder.addMenuPresenter(mExpandedMenuPresenter);
455        } else {
456            mActionMenuPresenter.initForMenu(mContext, null);
457            mExpandedMenuPresenter.initForMenu(mContext, null);
458            mActionMenuPresenter.updateMenuView(true);
459            mExpandedMenuPresenter.updateMenuView(true);
460        }
461    }
462
463    public boolean hasExpandedActionView() {
464        return mExpandedMenuPresenter != null &&
465                mExpandedMenuPresenter.mCurrentExpandedItem != null;
466    }
467
468    public void collapseActionView() {
469        final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
470                mExpandedMenuPresenter.mCurrentExpandedItem;
471        if (item != null) {
472            item.collapseActionView();
473        }
474    }
475
476    public void setCustomNavigationView(View view) {
477        final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
478        if (showCustom) {
479            ActionBarTransition.beginDelayedTransition(this);
480        }
481        if (mCustomNavView != null && showCustom) {
482            removeView(mCustomNavView);
483        }
484        mCustomNavView = view;
485        if (mCustomNavView != null && showCustom) {
486            addView(mCustomNavView);
487        }
488    }
489
490    public CharSequence getTitle() {
491        return mTitle;
492    }
493
494    /**
495     * Set the action bar title. This will always replace or override window titles.
496     * @param title Title to set
497     *
498     * @see #setWindowTitle(CharSequence)
499     */
500    public void setTitle(CharSequence title) {
501        mUserTitle = true;
502        setTitleImpl(title);
503    }
504
505    /**
506     * Set the window title. A window title will always be replaced or overridden by a user title.
507     * @param title Title to set
508     *
509     * @see #setTitle(CharSequence)
510     */
511    public void setWindowTitle(CharSequence title) {
512        if (!mUserTitle) {
513            setTitleImpl(title);
514        }
515    }
516
517    private void setTitleImpl(CharSequence title) {
518        ActionBarTransition.beginDelayedTransition(this);
519        mTitle = title;
520        if (mTitleView != null) {
521            mTitleView.setText(title);
522            final boolean visible = mExpandedActionView == null &&
523                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
524                    (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
525            mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
526        }
527        if (mLogoNavItem != null) {
528            mLogoNavItem.setTitle(title);
529        }
530        updateHomeAccessibility(mUpGoerFive.isEnabled());
531    }
532
533    public CharSequence getSubtitle() {
534        return mSubtitle;
535    }
536
537    public void setSubtitle(CharSequence subtitle) {
538        ActionBarTransition.beginDelayedTransition(this);
539        mSubtitle = subtitle;
540        if (mSubtitleView != null) {
541            mSubtitleView.setText(subtitle);
542            mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE);
543            final boolean visible = mExpandedActionView == null &&
544                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
545                    (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
546            mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
547        }
548        updateHomeAccessibility(mUpGoerFive.isEnabled());
549    }
550
551    public void setHomeButtonEnabled(boolean enable) {
552        setHomeButtonEnabled(enable, true);
553    }
554
555    private void setHomeButtonEnabled(boolean enable, boolean recordState) {
556        if (recordState) {
557            mWasHomeEnabled = enable;
558        }
559
560        if (mExpandedActionView != null) {
561            // There's an action view currently showing and we want to keep the state
562            // configured for the action view at the moment. If we needed to record the
563            // new state for later we will have done so above.
564            return;
565        }
566
567        mUpGoerFive.setEnabled(enable);
568        mUpGoerFive.setFocusable(enable);
569        // Make sure the home button has an accurate content description for accessibility.
570        updateHomeAccessibility(enable);
571    }
572
573    private void updateHomeAccessibility(boolean homeEnabled) {
574        if (!homeEnabled) {
575            mUpGoerFive.setContentDescription(null);
576            mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
577        } else {
578            mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
579            mUpGoerFive.setContentDescription(buildHomeContentDescription());
580        }
581    }
582
583    /**
584     * Compose a content description for the Home/Up affordance.
585     *
586     * <p>As this encompasses the icon/logo, title and subtitle all in one, we need
587     * a description for the whole wad of stuff that can be localized properly.</p>
588     */
589    private CharSequence buildHomeContentDescription() {
590        final CharSequence homeDesc;
591        if (mHomeDescription != null) {
592            homeDesc = mHomeDescription;
593        } else {
594            if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
595                homeDesc = mContext.getResources().getText(R.string.action_bar_up_description);
596            } else {
597                homeDesc = mContext.getResources().getText(R.string.action_bar_home_description);
598            }
599        }
600
601        final CharSequence title = getTitle();
602        final CharSequence subtitle = getSubtitle();
603        if (!TextUtils.isEmpty(title)) {
604            final String result;
605            if (!TextUtils.isEmpty(subtitle)) {
606                result = getResources().getString(
607                        R.string.action_bar_home_subtitle_description_format,
608                        title, subtitle, homeDesc);
609            } else {
610                result = getResources().getString(R.string.action_bar_home_description_format,
611                        title, homeDesc);
612            }
613            return result;
614        }
615        return homeDesc;
616    }
617
618    public void setDisplayOptions(int options) {
619        final int flagsChanged = mDisplayOptions == -1 ? -1 : options ^ mDisplayOptions;
620        mDisplayOptions = options;
621
622        if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
623            ActionBarTransition.beginDelayedTransition(this);
624
625            if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
626                final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0;
627                mHomeLayout.setShowUp(setUp);
628
629                // Showing home as up implicitly enables interaction with it.
630                // In honeycomb it was always enabled, so make this transition
631                // a bit easier for developers in the common case.
632                // (It would be silly to show it as up without responding to it.)
633                if (setUp) {
634                    setHomeButtonEnabled(true);
635                }
636            }
637
638            if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) {
639                final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0;
640                mHomeLayout.setIcon(logoVis ? mLogo : mIcon);
641            }
642
643            if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
644                if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
645                    initTitle();
646                } else {
647                    mUpGoerFive.removeView(mTitleLayout);
648                }
649            }
650
651            final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0;
652            final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
653            final boolean titleUp = !showHome && homeAsUp;
654            mHomeLayout.setShowIcon(showHome);
655
656            final int homeVis = (showHome || titleUp) && mExpandedActionView == null ?
657                    VISIBLE : GONE;
658            mHomeLayout.setVisibility(homeVis);
659
660            if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
661                if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
662                    addView(mCustomNavView);
663                } else {
664                    removeView(mCustomNavView);
665                }
666            }
667
668            if (mTitleLayout != null &&
669                    (flagsChanged & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) {
670                if ((options & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) {
671                    mTitleView.setSingleLine(false);
672                    mTitleView.setMaxLines(2);
673                } else {
674                    mTitleView.setMaxLines(1);
675                    mTitleView.setSingleLine(true);
676                }
677            }
678
679            requestLayout();
680        } else {
681            invalidate();
682        }
683
684        // Make sure the home button has an accurate content description for accessibility.
685        updateHomeAccessibility(mUpGoerFive.isEnabled());
686    }
687
688    public void setIcon(Drawable icon) {
689        mIcon = icon;
690        if (icon != null &&
691                ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) {
692            mHomeLayout.setIcon(icon);
693        }
694        if (mExpandedActionView != null) {
695            mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources()));
696        }
697    }
698
699    public void setIcon(int resId) {
700        setIcon(resId != 0 ? mContext.getDrawable(resId) : null);
701    }
702
703    public boolean hasIcon() {
704        return mIcon != null;
705    }
706
707    public void setLogo(Drawable logo) {
708        mLogo = logo;
709        if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
710            mHomeLayout.setIcon(logo);
711        }
712    }
713
714    public void setLogo(int resId) {
715        setLogo(resId != 0 ? mContext.getDrawable(resId) : null);
716    }
717
718    public boolean hasLogo() {
719        return mLogo != null;
720    }
721
722    public void setNavigationMode(int mode) {
723        final int oldMode = mNavigationMode;
724        if (mode != oldMode) {
725            ActionBarTransition.beginDelayedTransition(this);
726            switch (oldMode) {
727            case ActionBar.NAVIGATION_MODE_LIST:
728                if (mListNavLayout != null) {
729                    removeView(mListNavLayout);
730                }
731                break;
732            case ActionBar.NAVIGATION_MODE_TABS:
733                if (mTabScrollView != null && mIncludeTabs) {
734                    removeView(mTabScrollView);
735                }
736            }
737
738            switch (mode) {
739            case ActionBar.NAVIGATION_MODE_LIST:
740                if (mSpinner == null) {
741                    mSpinner = new Spinner(mContext, null,
742                            com.android.internal.R.attr.actionDropDownStyle);
743                    mSpinner.setId(com.android.internal.R.id.action_bar_spinner);
744                    mListNavLayout = new LinearLayout(mContext, null,
745                            com.android.internal.R.attr.actionBarTabBarStyle);
746                    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
747                            LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
748                    params.gravity = Gravity.CENTER;
749                    mListNavLayout.addView(mSpinner, params);
750                }
751                if (mSpinner.getAdapter() != mSpinnerAdapter) {
752                    mSpinner.setAdapter(mSpinnerAdapter);
753                }
754                mSpinner.setOnItemSelectedListener(mNavItemSelectedListener);
755                addView(mListNavLayout);
756                break;
757            case ActionBar.NAVIGATION_MODE_TABS:
758                if (mTabScrollView != null && mIncludeTabs) {
759                    addView(mTabScrollView);
760                }
761                break;
762            }
763            mNavigationMode = mode;
764            requestLayout();
765        }
766    }
767
768    public void setDropdownAdapter(SpinnerAdapter adapter) {
769        mSpinnerAdapter = adapter;
770        if (mSpinner != null) {
771            mSpinner.setAdapter(adapter);
772        }
773    }
774
775    public SpinnerAdapter getDropdownAdapter() {
776        return mSpinnerAdapter;
777    }
778
779    public void setDropdownSelectedPosition(int position) {
780        mSpinner.setSelection(position);
781    }
782
783    public int getDropdownSelectedPosition() {
784        return mSpinner.getSelectedItemPosition();
785    }
786
787    public View getCustomNavigationView() {
788        return mCustomNavView;
789    }
790
791    public int getNavigationMode() {
792        return mNavigationMode;
793    }
794
795    public int getDisplayOptions() {
796        return mDisplayOptions;
797    }
798
799    @Override
800    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
801        // Used by custom nav views if they don't supply layout params. Everything else
802        // added to an ActionBarView should have them already.
803        return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY);
804    }
805
806    @Override
807    protected void onFinishInflate() {
808        super.onFinishInflate();
809
810        mUpGoerFive.addView(mHomeLayout, 0);
811        addView(mUpGoerFive);
812
813        if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
814            final ViewParent parent = mCustomNavView.getParent();
815            if (parent != this) {
816                if (parent instanceof ViewGroup) {
817                    ((ViewGroup) parent).removeView(mCustomNavView);
818                }
819                addView(mCustomNavView);
820            }
821        }
822    }
823
824    private void initTitle() {
825        if (mTitleLayout == null) {
826            LayoutInflater inflater = LayoutInflater.from(getContext());
827            mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item,
828                    this, false);
829            mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
830            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
831
832            if (mTitleStyleRes != 0) {
833                mTitleView.setTextAppearance(mContext, mTitleStyleRes);
834            }
835            if (mTitle != null) {
836                mTitleView.setText(mTitle);
837            }
838
839            if (mSubtitleStyleRes != 0) {
840                mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
841            }
842            if (mSubtitle != null) {
843                mSubtitleView.setText(mSubtitle);
844                mSubtitleView.setVisibility(VISIBLE);
845            }
846        }
847
848        ActionBarTransition.beginDelayedTransition(this);
849        mUpGoerFive.addView(mTitleLayout);
850        if (mExpandedActionView != null ||
851                (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) {
852            // Don't show while in expanded mode or with empty text
853            mTitleLayout.setVisibility(GONE);
854        } else {
855            mTitleLayout.setVisibility(VISIBLE);
856        }
857    }
858
859    public void setContextView(ActionBarContextView view) {
860        mContextView = view;
861    }
862
863    public void setCollapsable(boolean collapsable) {
864        mIsCollapsable = collapsable;
865    }
866
867    public boolean isCollapsed() {
868        return mIsCollapsed;
869    }
870
871    /**
872     * @return True if any characters in the title were truncated
873     */
874    public boolean isTitleTruncated() {
875        if (mTitleView == null) {
876            return false;
877        }
878
879        final Layout titleLayout = mTitleView.getLayout();
880        if (titleLayout == null) {
881            return false;
882        }
883
884        final int lineCount = titleLayout.getLineCount();
885        for (int i = 0; i < lineCount; i++) {
886            if (titleLayout.getEllipsisCount(i) > 0) {
887                return true;
888            }
889        }
890        return false;
891    }
892
893    @Override
894    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
895        final int childCount = getChildCount();
896        if (mIsCollapsable) {
897            int visibleChildren = 0;
898            for (int i = 0; i < childCount; i++) {
899                final View child = getChildAt(i);
900                if (child.getVisibility() != GONE &&
901                        !(child == mMenuView && mMenuView.getChildCount() == 0) &&
902                        child != mUpGoerFive) {
903                    visibleChildren++;
904                }
905            }
906
907            final int upChildCount = mUpGoerFive.getChildCount();
908            for (int i = 0; i < upChildCount; i++) {
909                final View child = mUpGoerFive.getChildAt(i);
910                if (child.getVisibility() != GONE) {
911                    visibleChildren++;
912                }
913            }
914
915            if (visibleChildren == 0) {
916                // No size for an empty action bar when collapsable.
917                setMeasuredDimension(0, 0);
918                mIsCollapsed = true;
919                return;
920            }
921        }
922        mIsCollapsed = false;
923
924        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
925        if (widthMode != MeasureSpec.EXACTLY) {
926            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
927                    "with android:layout_width=\"match_parent\" (or fill_parent)");
928        }
929
930        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
931        if (heightMode != MeasureSpec.AT_MOST) {
932            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
933                    "with android:layout_height=\"wrap_content\"");
934        }
935
936        int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
937
938        int maxHeight = mContentHeight >= 0 ?
939                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
940
941        final int verticalPadding = getPaddingTop() + getPaddingBottom();
942        final int paddingLeft = getPaddingLeft();
943        final int paddingRight = getPaddingRight();
944        final int height = maxHeight - verticalPadding;
945        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
946        final int exactHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
947
948        int availableWidth = contentWidth - paddingLeft - paddingRight;
949        int leftOfCenter = availableWidth / 2;
950        int rightOfCenter = leftOfCenter;
951
952        final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
953                (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
954
955        HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
956
957        final ViewGroup.LayoutParams homeLp = homeLayout.getLayoutParams();
958        int homeWidthSpec;
959        if (homeLp.width < 0) {
960            homeWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
961        } else {
962            homeWidthSpec = MeasureSpec.makeMeasureSpec(homeLp.width, MeasureSpec.EXACTLY);
963        }
964
965        /*
966         * This is a little weird.
967         * We're only measuring the *home* affordance within the Up container here
968         * on purpose, because we want to give the available space to all other views before
969         * the title text. We'll remeasure the whole up container again later.
970         * We need to measure this container so we know the right offset for the up affordance
971         * no matter what.
972         */
973        homeLayout.measure(homeWidthSpec, exactHeightSpec);
974
975        int homeWidth = 0;
976        if ((homeLayout.getVisibility() != GONE && homeLayout.getParent() == mUpGoerFive)
977                || showTitle) {
978            homeWidth = homeLayout.getMeasuredWidth();
979            final int homeOffsetWidth = homeWidth + homeLayout.getStartOffset();
980            availableWidth = Math.max(0, availableWidth - homeOffsetWidth);
981            leftOfCenter = Math.max(0, availableWidth - homeOffsetWidth);
982        }
983
984        if (mMenuView != null && mMenuView.getParent() == this) {
985            availableWidth = measureChildView(mMenuView, availableWidth, exactHeightSpec, 0);
986            rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth());
987        }
988
989        if (mIndeterminateProgressView != null &&
990                mIndeterminateProgressView.getVisibility() != GONE) {
991            availableWidth = measureChildView(mIndeterminateProgressView, availableWidth,
992                    childSpecHeight, 0);
993            rightOfCenter = Math.max(0,
994                    rightOfCenter - mIndeterminateProgressView.getMeasuredWidth());
995        }
996
997        if (mExpandedActionView == null) {
998            switch (mNavigationMode) {
999                case ActionBar.NAVIGATION_MODE_LIST:
1000                    if (mListNavLayout != null) {
1001                        final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding;
1002                        availableWidth = Math.max(0, availableWidth - itemPaddingSize);
1003                        leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize);
1004                        mListNavLayout.measure(
1005                                MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
1006                                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
1007                        final int listNavWidth = mListNavLayout.getMeasuredWidth();
1008                        availableWidth = Math.max(0, availableWidth - listNavWidth);
1009                        leftOfCenter = Math.max(0, leftOfCenter - listNavWidth);
1010                    }
1011                    break;
1012                case ActionBar.NAVIGATION_MODE_TABS:
1013                    if (mTabScrollView != null) {
1014                        final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding;
1015                        availableWidth = Math.max(0, availableWidth - itemPaddingSize);
1016                        leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize);
1017                        mTabScrollView.measure(
1018                                MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
1019                                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
1020                        final int tabWidth = mTabScrollView.getMeasuredWidth();
1021                        availableWidth = Math.max(0, availableWidth - tabWidth);
1022                        leftOfCenter = Math.max(0, leftOfCenter - tabWidth);
1023                    }
1024                    break;
1025            }
1026        }
1027
1028        View customView = null;
1029        if (mExpandedActionView != null) {
1030            customView = mExpandedActionView;
1031        } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 &&
1032                mCustomNavView != null) {
1033            customView = mCustomNavView;
1034        }
1035
1036        if (customView != null) {
1037            final ViewGroup.LayoutParams lp = generateLayoutParams(customView.getLayoutParams());
1038            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
1039                    (ActionBar.LayoutParams) lp : null;
1040
1041            int horizontalMargin = 0;
1042            int verticalMargin = 0;
1043            if (ablp != null) {
1044                horizontalMargin = ablp.leftMargin + ablp.rightMargin;
1045                verticalMargin = ablp.topMargin + ablp.bottomMargin;
1046            }
1047
1048            // If the action bar is wrapping to its content height, don't allow a custom
1049            // view to MATCH_PARENT.
1050            int customNavHeightMode;
1051            if (mContentHeight <= 0) {
1052                customNavHeightMode = MeasureSpec.AT_MOST;
1053            } else {
1054                customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
1055                        MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
1056            }
1057            final int customNavHeight = Math.max(0,
1058                    (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin);
1059
1060            final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
1061                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
1062            int customNavWidth = Math.max(0,
1063                    (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth)
1064                    - horizontalMargin);
1065            final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) &
1066                    Gravity.HORIZONTAL_GRAVITY_MASK;
1067
1068            // Centering a custom view is treated specially; we try to center within the whole
1069            // action bar rather than in the available space.
1070            if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) {
1071                customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2;
1072            }
1073
1074            customView.measure(
1075                    MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode),
1076                    MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
1077            availableWidth -= horizontalMargin + customView.getMeasuredWidth();
1078        }
1079
1080        /*
1081         * Measure the whole up container now, allowing for the full home+title sections.
1082         * (This will re-measure the home view.)
1083         */
1084        availableWidth = measureChildView(mUpGoerFive, availableWidth + homeWidth,
1085                MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY), 0);
1086        if (mTitleLayout != null) {
1087            leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth());
1088        }
1089
1090        if (mContentHeight <= 0) {
1091            int measuredHeight = 0;
1092            for (int i = 0; i < childCount; i++) {
1093                View v = getChildAt(i);
1094                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
1095                if (paddedViewHeight > measuredHeight) {
1096                    measuredHeight = paddedViewHeight;
1097                }
1098            }
1099            setMeasuredDimension(contentWidth, measuredHeight);
1100        } else {
1101            setMeasuredDimension(contentWidth, maxHeight);
1102        }
1103
1104        if (mContextView != null) {
1105            mContextView.setContentHeight(getMeasuredHeight());
1106        }
1107
1108        if (mProgressView != null && mProgressView.getVisibility() != GONE) {
1109            mProgressView.measure(MeasureSpec.makeMeasureSpec(
1110                    contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY),
1111                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST));
1112        }
1113    }
1114
1115    @Override
1116    protected void onLayout(boolean changed, int l, int t, int r, int b) {
1117        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
1118
1119        if (contentHeight <= 0) {
1120            // Nothing to do if we can't see anything.
1121            return;
1122        }
1123
1124        final boolean isLayoutRtl = isLayoutRtl();
1125        final int direction = isLayoutRtl ? 1 : -1;
1126        int menuStart = isLayoutRtl ? getPaddingLeft() : r - l - getPaddingRight();
1127        // In LTR mode, we start from left padding and go to the right; in RTL mode, we start
1128        // from the padding right and go to the left (in reverse way)
1129        int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft();
1130        final int y = getPaddingTop();
1131
1132        HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
1133        final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
1134                (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
1135        int startOffset = 0;
1136        if (homeLayout.getParent() == mUpGoerFive) {
1137            if (homeLayout.getVisibility() != GONE) {
1138                startOffset = homeLayout.getStartOffset();
1139            } else if (showTitle) {
1140                startOffset = homeLayout.getUpWidth();
1141            }
1142        }
1143
1144        // Position the up container based on where the edge of the home layout should go.
1145        x += positionChild(mUpGoerFive,
1146                next(x, startOffset, isLayoutRtl), y, contentHeight, isLayoutRtl);
1147        x = next(x, startOffset, isLayoutRtl);
1148
1149        if (mExpandedActionView == null) {
1150            switch (mNavigationMode) {
1151                case ActionBar.NAVIGATION_MODE_STANDARD:
1152                    break;
1153                case ActionBar.NAVIGATION_MODE_LIST:
1154                    if (mListNavLayout != null) {
1155                        if (showTitle) {
1156                            x = next(x, mItemPadding, isLayoutRtl);
1157                        }
1158                        x += positionChild(mListNavLayout, x, y, contentHeight, isLayoutRtl);
1159                        x = next(x, mItemPadding, isLayoutRtl);
1160                    }
1161                    break;
1162                case ActionBar.NAVIGATION_MODE_TABS:
1163                    if (mTabScrollView != null) {
1164                        if (showTitle) x = next(x, mItemPadding, isLayoutRtl);
1165                        x += positionChild(mTabScrollView, x, y, contentHeight, isLayoutRtl);
1166                        x = next(x, mItemPadding, isLayoutRtl);
1167                    }
1168                    break;
1169            }
1170        }
1171
1172        if (mMenuView != null && mMenuView.getParent() == this) {
1173            positionChild(mMenuView, menuStart, y, contentHeight, !isLayoutRtl);
1174            menuStart += direction * mMenuView.getMeasuredWidth();
1175        }
1176
1177        if (mIndeterminateProgressView != null &&
1178                mIndeterminateProgressView.getVisibility() != GONE) {
1179            positionChild(mIndeterminateProgressView, menuStart, y, contentHeight, !isLayoutRtl);
1180            menuStart += direction * mIndeterminateProgressView.getMeasuredWidth();
1181        }
1182
1183        View customView = null;
1184        if (mExpandedActionView != null) {
1185            customView = mExpandedActionView;
1186        } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 &&
1187                mCustomNavView != null) {
1188            customView = mCustomNavView;
1189        }
1190        if (customView != null) {
1191            final int layoutDirection = getLayoutDirection();
1192            ViewGroup.LayoutParams lp = customView.getLayoutParams();
1193            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
1194                    (ActionBar.LayoutParams) lp : null;
1195            final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
1196            final int navWidth = customView.getMeasuredWidth();
1197
1198            int topMargin = 0;
1199            int bottomMargin = 0;
1200            if (ablp != null) {
1201                x = next(x, ablp.getMarginStart(), isLayoutRtl);
1202                menuStart += direction * ablp.getMarginEnd();
1203                topMargin = ablp.topMargin;
1204                bottomMargin = ablp.bottomMargin;
1205            }
1206
1207            int hgravity = gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1208            // See if we actually have room to truly center; if not push against left or right.
1209            if (hgravity == Gravity.CENTER_HORIZONTAL) {
1210                final int centeredLeft = ((mRight - mLeft) - navWidth) / 2;
1211                if (isLayoutRtl) {
1212                    final int centeredStart = centeredLeft + navWidth;
1213                    final int centeredEnd = centeredLeft;
1214                    if (centeredStart > x) {
1215                        hgravity = Gravity.RIGHT;
1216                    } else if (centeredEnd < menuStart) {
1217                        hgravity = Gravity.LEFT;
1218                    }
1219                } else {
1220                    final int centeredStart = centeredLeft;
1221                    final int centeredEnd = centeredLeft + navWidth;
1222                    if (centeredStart < x) {
1223                        hgravity = Gravity.LEFT;
1224                    } else if (centeredEnd > menuStart) {
1225                        hgravity = Gravity.RIGHT;
1226                    }
1227                }
1228            } else if (gravity == Gravity.NO_GRAVITY) {
1229                hgravity = Gravity.START;
1230            }
1231
1232            int xpos = 0;
1233            switch (Gravity.getAbsoluteGravity(hgravity, layoutDirection)) {
1234                case Gravity.CENTER_HORIZONTAL:
1235                    xpos = ((mRight - mLeft) - navWidth) / 2;
1236                    break;
1237                case Gravity.LEFT:
1238                    xpos = isLayoutRtl ? menuStart : x;
1239                    break;
1240                case Gravity.RIGHT:
1241                    xpos = isLayoutRtl ? x - navWidth : menuStart - navWidth;
1242                    break;
1243            }
1244
1245            int vgravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
1246
1247            if (gravity == Gravity.NO_GRAVITY) {
1248                vgravity = Gravity.CENTER_VERTICAL;
1249            }
1250
1251            int ypos = 0;
1252            switch (vgravity) {
1253                case Gravity.CENTER_VERTICAL:
1254                    final int paddedTop = getPaddingTop();
1255                    final int paddedBottom = mBottom - mTop - getPaddingBottom();
1256                    ypos = ((paddedBottom - paddedTop) - customView.getMeasuredHeight()) / 2;
1257                    break;
1258                case Gravity.TOP:
1259                    ypos = getPaddingTop() + topMargin;
1260                    break;
1261                case Gravity.BOTTOM:
1262                    ypos = getHeight() - getPaddingBottom() - customView.getMeasuredHeight()
1263                            - bottomMargin;
1264                    break;
1265            }
1266            final int customWidth = customView.getMeasuredWidth();
1267            customView.layout(xpos, ypos, xpos + customWidth,
1268                    ypos + customView.getMeasuredHeight());
1269            x = next(x, customWidth, isLayoutRtl);
1270        }
1271
1272        if (mProgressView != null) {
1273            mProgressView.bringToFront();
1274            final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2;
1275            mProgressView.layout(mProgressBarPadding, -halfProgressHeight,
1276                    mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight);
1277        }
1278    }
1279
1280    @Override
1281    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
1282        return new ActionBar.LayoutParams(getContext(), attrs);
1283    }
1284
1285    @Override
1286    public ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
1287        if (lp == null) {
1288            lp = generateDefaultLayoutParams();
1289        }
1290        return lp;
1291    }
1292
1293    @Override
1294    public Parcelable onSaveInstanceState() {
1295        Parcelable superState = super.onSaveInstanceState();
1296        SavedState state = new SavedState(superState);
1297
1298        if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) {
1299            state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId();
1300        }
1301
1302        state.isOverflowOpen = isOverflowMenuShowing();
1303
1304        return state;
1305    }
1306
1307    @Override
1308    public void onRestoreInstanceState(Parcelable p) {
1309        SavedState state = (SavedState) p;
1310
1311        super.onRestoreInstanceState(state.getSuperState());
1312
1313        if (state.expandedMenuItemId != 0 &&
1314                mExpandedMenuPresenter != null && mOptionsMenu != null) {
1315            final MenuItem item = mOptionsMenu.findItem(state.expandedMenuItemId);
1316            if (item != null) {
1317                item.expandActionView();
1318            }
1319        }
1320
1321        if (state.isOverflowOpen) {
1322            postShowOverflowMenu();
1323        }
1324    }
1325
1326    public void setHomeAsUpIndicator(Drawable indicator) {
1327        mHomeLayout.setUpIndicator(indicator);
1328    }
1329
1330    public void setHomeAsUpIndicator(int resId) {
1331        mHomeLayout.setUpIndicator(resId);
1332    }
1333
1334    public void setHomeActionContentDescription(CharSequence description) {
1335        mHomeDescription = description;
1336        updateHomeAccessibility(mUpGoerFive.isEnabled());
1337    }
1338
1339    public void setHomeActionContentDescription(int resId) {
1340        mHomeDescriptionRes = resId;
1341        mHomeDescription = resId != 0 ? getResources().getText(resId) : null;
1342        updateHomeAccessibility(mUpGoerFive.isEnabled());
1343    }
1344
1345    static class SavedState extends BaseSavedState {
1346        int expandedMenuItemId;
1347        boolean isOverflowOpen;
1348
1349        SavedState(Parcelable superState) {
1350            super(superState);
1351        }
1352
1353        private SavedState(Parcel in) {
1354            super(in);
1355            expandedMenuItemId = in.readInt();
1356            isOverflowOpen = in.readInt() != 0;
1357        }
1358
1359        @Override
1360        public void writeToParcel(Parcel out, int flags) {
1361            super.writeToParcel(out, flags);
1362            out.writeInt(expandedMenuItemId);
1363            out.writeInt(isOverflowOpen ? 1 : 0);
1364        }
1365
1366        public static final Parcelable.Creator<SavedState> CREATOR =
1367                new Parcelable.Creator<SavedState>() {
1368            public SavedState createFromParcel(Parcel in) {
1369                return new SavedState(in);
1370            }
1371
1372            public SavedState[] newArray(int size) {
1373                return new SavedState[size];
1374            }
1375        };
1376    }
1377
1378    private static class HomeView extends FrameLayout {
1379        private ImageView mUpView;
1380        private ImageView mIconView;
1381        private int mUpWidth;
1382        private int mStartOffset;
1383        private int mUpIndicatorRes;
1384        private Drawable mDefaultUpIndicator;
1385
1386        private static final long DEFAULT_TRANSITION_DURATION = 150;
1387
1388        public HomeView(Context context) {
1389            this(context, null);
1390        }
1391
1392        public HomeView(Context context, AttributeSet attrs) {
1393            super(context, attrs);
1394            LayoutTransition t = getLayoutTransition();
1395            if (t != null) {
1396                // Set a lower duration than the default
1397                t.setDuration(DEFAULT_TRANSITION_DURATION);
1398            }
1399        }
1400
1401        public void setShowUp(boolean isUp) {
1402            mUpView.setVisibility(isUp ? VISIBLE : GONE);
1403        }
1404
1405        public void setShowIcon(boolean showIcon) {
1406            mIconView.setVisibility(showIcon ? VISIBLE : GONE);
1407        }
1408
1409        public void setIcon(Drawable icon) {
1410            mIconView.setImageDrawable(icon);
1411        }
1412
1413        public void setUpIndicator(Drawable d) {
1414            mUpView.setImageDrawable(d != null ? d : mDefaultUpIndicator);
1415            mUpIndicatorRes = 0;
1416        }
1417
1418        public void setUpIndicator(int resId) {
1419            mUpIndicatorRes = resId;
1420            mUpView.setImageDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
1421        }
1422
1423        @Override
1424        protected void onConfigurationChanged(Configuration newConfig) {
1425            super.onConfigurationChanged(newConfig);
1426            if (mUpIndicatorRes != 0) {
1427                // Reload for config change
1428                setUpIndicator(mUpIndicatorRes);
1429            }
1430        }
1431
1432        @Override
1433        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1434            onPopulateAccessibilityEvent(event);
1435            return true;
1436        }
1437
1438        @Override
1439        public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
1440            super.onPopulateAccessibilityEvent(event);
1441            final CharSequence cdesc = getContentDescription();
1442            if (!TextUtils.isEmpty(cdesc)) {
1443                event.getText().add(cdesc);
1444            }
1445        }
1446
1447        @Override
1448        public boolean dispatchHoverEvent(MotionEvent event) {
1449            // Don't allow children to hover; we want this to be treated as a single component.
1450            return onHoverEvent(event);
1451        }
1452
1453        @Override
1454        protected void onFinishInflate() {
1455            mUpView = (ImageView) findViewById(com.android.internal.R.id.up);
1456            mIconView = (ImageView) findViewById(com.android.internal.R.id.home);
1457            mDefaultUpIndicator = mUpView.getDrawable();
1458        }
1459
1460        public int getStartOffset() {
1461            return mUpView.getVisibility() == GONE ? mStartOffset : 0;
1462        }
1463
1464        public int getUpWidth() {
1465            return mUpWidth;
1466        }
1467
1468        @Override
1469        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1470            measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
1471            final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
1472            final int upMargins = upLp.leftMargin + upLp.rightMargin;
1473            mUpWidth = mUpView.getMeasuredWidth();
1474            mStartOffset = mUpWidth + upMargins;
1475            int width = mUpView.getVisibility() == GONE ? 0 : mStartOffset;
1476            int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
1477
1478            if (mIconView.getVisibility() != GONE) {
1479                measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
1480                final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
1481                width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
1482                height = Math.max(height,
1483                        iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin);
1484            } else if (upMargins < 0) {
1485                // Remove the measurement effects of negative margins used for offsets
1486                width -= upMargins;
1487            }
1488
1489            final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
1490            final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
1491            final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
1492            final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
1493
1494            switch (widthMode) {
1495                case MeasureSpec.AT_MOST:
1496                    width = Math.min(width, widthSize);
1497                    break;
1498                case MeasureSpec.EXACTLY:
1499                    width = widthSize;
1500                    break;
1501                case MeasureSpec.UNSPECIFIED:
1502                default:
1503                    break;
1504            }
1505            switch (heightMode) {
1506                case MeasureSpec.AT_MOST:
1507                    height = Math.min(height, heightSize);
1508                    break;
1509                case MeasureSpec.EXACTLY:
1510                    height = heightSize;
1511                    break;
1512                case MeasureSpec.UNSPECIFIED:
1513                default:
1514                    break;
1515            }
1516            setMeasuredDimension(width, height);
1517        }
1518
1519        @Override
1520        protected void onLayout(boolean changed, int l, int t, int r, int b) {
1521            final int vCenter = (b - t) / 2;
1522            final boolean isLayoutRtl = isLayoutRtl();
1523            final int width = getWidth();
1524            int upOffset = 0;
1525            if (mUpView.getVisibility() != GONE) {
1526                final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
1527                final int upHeight = mUpView.getMeasuredHeight();
1528                final int upWidth = mUpView.getMeasuredWidth();
1529                upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
1530                final int upTop = vCenter - upHeight / 2;
1531                final int upBottom = upTop + upHeight;
1532                final int upRight;
1533                final int upLeft;
1534                if (isLayoutRtl) {
1535                    upRight = width;
1536                    upLeft = upRight - upWidth;
1537                    r -= upOffset;
1538                } else {
1539                    upRight = upWidth;
1540                    upLeft = 0;
1541                    l += upOffset;
1542                }
1543                mUpView.layout(upLeft, upTop, upRight, upBottom);
1544            }
1545
1546            final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
1547            final int iconHeight = mIconView.getMeasuredHeight();
1548            final int iconWidth = mIconView.getMeasuredWidth();
1549            final int hCenter = (r - l) / 2;
1550            final int iconTop = Math.max(iconLp.topMargin, vCenter - iconHeight / 2);
1551            final int iconBottom = iconTop + iconHeight;
1552            final int iconLeft;
1553            final int iconRight;
1554            int marginStart = iconLp.getMarginStart();
1555            final int delta = Math.max(marginStart, hCenter - iconWidth / 2);
1556            if (isLayoutRtl) {
1557                iconRight = width - upOffset - delta;
1558                iconLeft = iconRight - iconWidth;
1559            } else {
1560                iconLeft = upOffset + delta;
1561                iconRight = iconLeft + iconWidth;
1562            }
1563            mIconView.layout(iconLeft, iconTop, iconRight, iconBottom);
1564        }
1565    }
1566
1567    private class ExpandedActionViewMenuPresenter implements MenuPresenter {
1568        MenuBuilder mMenu;
1569        MenuItemImpl mCurrentExpandedItem;
1570
1571        @Override
1572        public void initForMenu(Context context, MenuBuilder menu) {
1573            // Clear the expanded action view when menus change.
1574            if (mMenu != null && mCurrentExpandedItem != null) {
1575                mMenu.collapseItemActionView(mCurrentExpandedItem);
1576            }
1577            mMenu = menu;
1578        }
1579
1580        @Override
1581        public MenuView getMenuView(ViewGroup root) {
1582            return null;
1583        }
1584
1585        @Override
1586        public void updateMenuView(boolean cleared) {
1587            // Make sure the expanded item we have is still there.
1588            if (mCurrentExpandedItem != null) {
1589                boolean found = false;
1590
1591                if (mMenu != null) {
1592                    final int count = mMenu.size();
1593                    for (int i = 0; i < count; i++) {
1594                        final MenuItem item = mMenu.getItem(i);
1595                        if (item == mCurrentExpandedItem) {
1596                            found = true;
1597                            break;
1598                        }
1599                    }
1600                }
1601
1602                if (!found) {
1603                    // The item we had expanded disappeared. Collapse.
1604                    collapseItemActionView(mMenu, mCurrentExpandedItem);
1605                }
1606            }
1607        }
1608
1609        @Override
1610        public void setCallback(Callback cb) {
1611        }
1612
1613        @Override
1614        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
1615            return false;
1616        }
1617
1618        @Override
1619        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1620        }
1621
1622        @Override
1623        public boolean flagActionItems() {
1624            return false;
1625        }
1626
1627        @Override
1628        public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
1629            ActionBarTransition.beginDelayedTransition(ActionBarView.this);
1630
1631            mExpandedActionView = item.getActionView();
1632            mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources()));
1633            mCurrentExpandedItem = item;
1634            if (mExpandedActionView.getParent() != ActionBarView.this) {
1635                addView(mExpandedActionView);
1636            }
1637            if (mExpandedHomeLayout.getParent() != mUpGoerFive) {
1638                mUpGoerFive.addView(mExpandedHomeLayout);
1639            }
1640            mHomeLayout.setVisibility(GONE);
1641            if (mTitleLayout != null) mTitleLayout.setVisibility(GONE);
1642            if (mTabScrollView != null) mTabScrollView.setVisibility(GONE);
1643            if (mSpinner != null) mSpinner.setVisibility(GONE);
1644            if (mCustomNavView != null) mCustomNavView.setVisibility(GONE);
1645            setHomeButtonEnabled(false, false);
1646            requestLayout();
1647            item.setActionViewExpanded(true);
1648
1649            if (mExpandedActionView instanceof CollapsibleActionView) {
1650                ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
1651            }
1652
1653            return true;
1654        }
1655
1656        @Override
1657        public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
1658            ActionBarTransition.beginDelayedTransition(ActionBarView.this);
1659
1660            // Do this before detaching the actionview from the hierarchy, in case
1661            // it needs to dismiss the soft keyboard, etc.
1662            if (mExpandedActionView instanceof CollapsibleActionView) {
1663                ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
1664            }
1665
1666            removeView(mExpandedActionView);
1667            mUpGoerFive.removeView(mExpandedHomeLayout);
1668            mExpandedActionView = null;
1669            if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) {
1670                mHomeLayout.setVisibility(VISIBLE);
1671            }
1672            if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
1673                if (mTitleLayout == null) {
1674                    initTitle();
1675                } else {
1676                    mTitleLayout.setVisibility(VISIBLE);
1677                }
1678            }
1679            if (mTabScrollView != null) mTabScrollView.setVisibility(VISIBLE);
1680            if (mSpinner != null) mSpinner.setVisibility(VISIBLE);
1681            if (mCustomNavView != null) mCustomNavView.setVisibility(VISIBLE);
1682
1683            mExpandedHomeLayout.setIcon(null);
1684            mCurrentExpandedItem = null;
1685            setHomeButtonEnabled(mWasHomeEnabled); // Set by expandItemActionView above
1686            requestLayout();
1687            item.setActionViewExpanded(false);
1688
1689            return true;
1690        }
1691
1692        @Override
1693        public int getId() {
1694            return 0;
1695        }
1696
1697        @Override
1698        public Parcelable onSaveInstanceState() {
1699            return null;
1700        }
1701
1702        @Override
1703        public void onRestoreInstanceState(Parcelable state) {
1704        }
1705    }
1706}
1707