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