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