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