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