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