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