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