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