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