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