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