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