ToolbarActionBar.java revision c46125042493e66f7dfdccec24e3b0739600b194
1/* 2 * Copyright (C) 2014 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 18package com.android.internal.app; 19 20import android.annotation.Nullable; 21import android.app.ActionBar; 22import android.content.Context; 23import android.content.res.Configuration; 24import android.graphics.drawable.Drawable; 25import android.text.TextUtils; 26import android.view.ActionMode; 27import android.view.KeyEvent; 28import android.view.LayoutInflater; 29import android.view.Menu; 30import android.view.MenuItem; 31import android.view.View; 32import android.view.Window; 33import android.view.WindowCallbackWrapper; 34import android.widget.SpinnerAdapter; 35import android.widget.Toolbar; 36import com.android.internal.R; 37import com.android.internal.view.menu.MenuBuilder; 38import com.android.internal.view.menu.MenuPresenter; 39import com.android.internal.widget.DecorToolbar; 40import com.android.internal.widget.ToolbarWidgetWrapper; 41 42import java.util.ArrayList; 43 44public class ToolbarActionBar extends ActionBar { 45 private Toolbar mToolbar; 46 private DecorToolbar mDecorToolbar; 47 private boolean mToolbarMenuPrepared; 48 private Window.Callback mWindowCallback; 49 private boolean mMenuCallbackSet; 50 51 private CharSequence mHomeDescription; 52 53 private boolean mLastMenuVisibility; 54 private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = 55 new ArrayList<OnMenuVisibilityListener>(); 56 57 private final Runnable mMenuInvalidator = new Runnable() { 58 @Override 59 public void run() { 60 populateOptionsMenu(); 61 } 62 }; 63 64 private final Toolbar.OnMenuItemClickListener mMenuClicker = 65 new Toolbar.OnMenuItemClickListener() { 66 @Override 67 public boolean onMenuItemClick(MenuItem item) { 68 return mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); 69 } 70 }; 71 72 public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) { 73 mToolbar = toolbar; 74 mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false); 75 mWindowCallback = new ToolbarCallbackWrapper(windowCallback); 76 mDecorToolbar.setWindowCallback(mWindowCallback); 77 toolbar.setOnMenuItemClickListener(mMenuClicker); 78 mDecorToolbar.setWindowTitle(title); 79 mHomeDescription = mToolbar.getNavigationContentDescription(); 80 updateNavDescription(); 81 } 82 83 public Window.Callback getWrappedWindowCallback() { 84 return mWindowCallback; 85 } 86 87 @Override 88 public void setCustomView(View view) { 89 setCustomView(view, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 90 } 91 92 @Override 93 public void setCustomView(View view, LayoutParams layoutParams) { 94 view.setLayoutParams(layoutParams); 95 mDecorToolbar.setCustomView(view); 96 } 97 98 @Override 99 public void setCustomView(int resId) { 100 final LayoutInflater inflater = LayoutInflater.from(mToolbar.getContext()); 101 setCustomView(inflater.inflate(resId, mToolbar, false)); 102 } 103 104 @Override 105 public void setIcon(int resId) { 106 mDecorToolbar.setIcon(resId); 107 } 108 109 @Override 110 public void setIcon(Drawable icon) { 111 mDecorToolbar.setIcon(icon); 112 } 113 114 @Override 115 public void setLogo(int resId) { 116 mDecorToolbar.setLogo(resId); 117 } 118 119 @Override 120 public void setLogo(Drawable logo) { 121 mDecorToolbar.setLogo(logo); 122 } 123 124 @Override 125 public void setStackedBackgroundDrawable(Drawable d) { 126 // This space for rent (do nothing) 127 } 128 129 @Override 130 public void setSplitBackgroundDrawable(Drawable d) { 131 // This space for rent (do nothing) 132 } 133 134 @Override 135 public void setHomeButtonEnabled(boolean enabled) { 136 // If the nav button on a Toolbar is present, it's enabled. No-op. 137 } 138 139 @Override 140 public void setElevation(float elevation) { 141 mToolbar.setElevation(elevation); 142 } 143 144 @Override 145 public float getElevation() { 146 return mToolbar.getElevation(); 147 } 148 149 @Override 150 public Context getThemedContext() { 151 return mToolbar.getContext(); 152 } 153 154 @Override 155 public boolean isTitleTruncated() { 156 return super.isTitleTruncated(); 157 } 158 159 @Override 160 public void setHomeAsUpIndicator(Drawable indicator) { 161 mToolbar.setNavigationIcon(indicator); 162 } 163 164 @Override 165 public void setHomeAsUpIndicator(int resId) { 166 mToolbar.setNavigationIcon(resId); 167 } 168 169 @Override 170 public void setHomeActionContentDescription(CharSequence description) { 171 mToolbar.setNavigationContentDescription(description); 172 mHomeDescription = description; 173 } 174 175 @Override 176 public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) { 177 // Do nothing 178 } 179 180 @Override 181 public void setHomeActionContentDescription(int resId) { 182 mToolbar.setNavigationContentDescription(resId); 183 mHomeDescription = mToolbar.getNavigationContentDescription(); 184 } 185 186 @Override 187 public void setShowHideAnimationEnabled(boolean enabled) { 188 // This space for rent; no-op. 189 } 190 191 @Override 192 public void onConfigurationChanged(Configuration config) { 193 super.onConfigurationChanged(config); 194 } 195 196 @Override 197 public ActionMode startActionMode(ActionMode.Callback callback) { 198 return null; 199 } 200 201 @Override 202 public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { 203 mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback)); 204 } 205 206 @Override 207 public void setSelectedNavigationItem(int position) { 208 switch (mDecorToolbar.getNavigationMode()) { 209 case NAVIGATION_MODE_LIST: 210 mDecorToolbar.setDropdownSelectedPosition(position); 211 break; 212 default: 213 throw new IllegalStateException( 214 "setSelectedNavigationIndex not valid for current navigation mode"); 215 } 216 } 217 218 @Override 219 public int getSelectedNavigationIndex() { 220 return -1; 221 } 222 223 @Override 224 public int getNavigationItemCount() { 225 return 0; 226 } 227 228 @Override 229 public void setTitle(CharSequence title) { 230 mDecorToolbar.setTitle(title); 231 } 232 233 @Override 234 public void setTitle(int resId) { 235 mDecorToolbar.setTitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null); 236 } 237 238 @Override 239 public void setWindowTitle(CharSequence title) { 240 mDecorToolbar.setWindowTitle(title); 241 } 242 243 @Override 244 public void setSubtitle(CharSequence subtitle) { 245 mDecorToolbar.setSubtitle(subtitle); 246 } 247 248 @Override 249 public void setSubtitle(int resId) { 250 mDecorToolbar.setSubtitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null); 251 } 252 253 @Override 254 public void setDisplayOptions(@DisplayOptions int options) { 255 setDisplayOptions(options, 0xffffffff); 256 } 257 258 @Override 259 public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) { 260 final int currentOptions = mDecorToolbar.getDisplayOptions(); 261 final int changed = (options ^ currentOptions) & mask; 262 mDecorToolbar.setDisplayOptions(options & mask | currentOptions & ~mask); 263 if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 264 updateNavDescription(); 265 } 266 } 267 268 private void updateNavDescription() { 269 if ((mDecorToolbar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 270 if (TextUtils.isEmpty(mHomeDescription)) { 271 mToolbar.setNavigationContentDescription(R.string.action_bar_up_description); 272 } else { 273 mToolbar.setNavigationContentDescription(mHomeDescription); 274 } 275 } 276 } 277 278 @Override 279 public void setDisplayUseLogoEnabled(boolean useLogo) { 280 setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO); 281 } 282 283 @Override 284 public void setDisplayShowHomeEnabled(boolean showHome) { 285 setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME); 286 } 287 288 @Override 289 public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { 290 setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP); 291 } 292 293 @Override 294 public void setDisplayShowTitleEnabled(boolean showTitle) { 295 setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE); 296 } 297 298 @Override 299 public void setDisplayShowCustomEnabled(boolean showCustom) { 300 setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM); 301 } 302 303 @Override 304 public void setBackgroundDrawable(@Nullable Drawable d) { 305 mToolbar.setBackground(d); 306 } 307 308 @Override 309 public View getCustomView() { 310 return mDecorToolbar.getCustomView(); 311 } 312 313 @Override 314 public CharSequence getTitle() { 315 return mToolbar.getTitle(); 316 } 317 318 @Override 319 public CharSequence getSubtitle() { 320 return mToolbar.getSubtitle(); 321 } 322 323 @Override 324 public int getNavigationMode() { 325 return NAVIGATION_MODE_STANDARD; 326 } 327 328 @Override 329 public void setNavigationMode(@NavigationMode int mode) { 330 if (mode == ActionBar.NAVIGATION_MODE_TABS) { 331 throw new IllegalArgumentException("Tabs not supported in this configuration"); 332 } 333 mDecorToolbar.setNavigationMode(mode); 334 } 335 336 @Override 337 public int getDisplayOptions() { 338 return mDecorToolbar.getDisplayOptions(); 339 } 340 341 @Override 342 public Tab newTab() { 343 throw new UnsupportedOperationException( 344 "Tabs are not supported in toolbar action bars"); 345 } 346 347 @Override 348 public void addTab(Tab tab) { 349 throw new UnsupportedOperationException( 350 "Tabs are not supported in toolbar action bars"); 351 } 352 353 @Override 354 public void addTab(Tab tab, boolean setSelected) { 355 throw new UnsupportedOperationException( 356 "Tabs are not supported in toolbar action bars"); 357 } 358 359 @Override 360 public void addTab(Tab tab, int position) { 361 throw new UnsupportedOperationException( 362 "Tabs are not supported in toolbar action bars"); 363 } 364 365 @Override 366 public void addTab(Tab tab, int position, boolean setSelected) { 367 throw new UnsupportedOperationException( 368 "Tabs are not supported in toolbar action bars"); 369 } 370 371 @Override 372 public void removeTab(Tab tab) { 373 throw new UnsupportedOperationException( 374 "Tabs are not supported in toolbar action bars"); 375 } 376 377 @Override 378 public void removeTabAt(int position) { 379 throw new UnsupportedOperationException( 380 "Tabs are not supported in toolbar action bars"); 381 } 382 383 @Override 384 public void removeAllTabs() { 385 throw new UnsupportedOperationException( 386 "Tabs are not supported in toolbar action bars"); 387 } 388 389 @Override 390 public void selectTab(Tab tab) { 391 throw new UnsupportedOperationException( 392 "Tabs are not supported in toolbar action bars"); 393 } 394 395 @Override 396 public Tab getSelectedTab() { 397 throw new UnsupportedOperationException( 398 "Tabs are not supported in toolbar action bars"); 399 } 400 401 @Override 402 public Tab getTabAt(int index) { 403 throw new UnsupportedOperationException( 404 "Tabs are not supported in toolbar action bars"); 405 } 406 407 @Override 408 public int getTabCount() { 409 return 0; 410 } 411 412 @Override 413 public int getHeight() { 414 return mToolbar.getHeight(); 415 } 416 417 @Override 418 public void show() { 419 // TODO: Consider a better transition for this. 420 // Right now use no automatic transition so that the app can supply one if desired. 421 mToolbar.setVisibility(View.VISIBLE); 422 } 423 424 @Override 425 public void hide() { 426 // TODO: Consider a better transition for this. 427 // Right now use no automatic transition so that the app can supply one if desired. 428 mToolbar.setVisibility(View.GONE); 429 } 430 431 @Override 432 public boolean isShowing() { 433 return mToolbar.getVisibility() == View.VISIBLE; 434 } 435 436 @Override 437 public boolean openOptionsMenu() { 438 return mToolbar.showOverflowMenu(); 439 } 440 441 @Override 442 public boolean invalidateOptionsMenu() { 443 mToolbar.removeCallbacks(mMenuInvalidator); 444 mToolbar.postOnAnimation(mMenuInvalidator); 445 return true; 446 } 447 448 @Override 449 public boolean collapseActionView() { 450 if (mToolbar.hasExpandedActionView()) { 451 mToolbar.collapseActionView(); 452 return true; 453 } 454 return false; 455 } 456 457 void populateOptionsMenu() { 458 if (!mMenuCallbackSet) { 459 mToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(), new MenuBuilderCallback()); 460 mMenuCallbackSet = true; 461 } 462 final Menu menu = mToolbar.getMenu(); 463 final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null; 464 if (mb != null) { 465 mb.stopDispatchingItemsChanged(); 466 } 467 try { 468 menu.clear(); 469 if (!mWindowCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) || 470 !mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) { 471 menu.clear(); 472 } 473 } finally { 474 if (mb != null) { 475 mb.startDispatchingItemsChanged(); 476 } 477 } 478 } 479 480 @Override 481 public boolean onMenuKeyEvent(KeyEvent event) { 482 if (event.getAction() == KeyEvent.ACTION_UP) { 483 openOptionsMenu(); 484 } 485 return true; 486 } 487 488 public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { 489 mMenuVisibilityListeners.add(listener); 490 } 491 492 public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { 493 mMenuVisibilityListeners.remove(listener); 494 } 495 496 public void dispatchMenuVisibilityChanged(boolean isVisible) { 497 if (isVisible == mLastMenuVisibility) { 498 return; 499 } 500 mLastMenuVisibility = isVisible; 501 502 final int count = mMenuVisibilityListeners.size(); 503 for (int i = 0; i < count; i++) { 504 mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible); 505 } 506 } 507 508 private class ToolbarCallbackWrapper extends WindowCallbackWrapper { 509 public ToolbarCallbackWrapper(Window.Callback wrapped) { 510 super(wrapped); 511 } 512 513 @Override 514 public boolean onPreparePanel(int featureId, View view, Menu menu) { 515 final boolean result = super.onPreparePanel(featureId, view, menu); 516 if (result && !mToolbarMenuPrepared) { 517 mDecorToolbar.setMenuPrepared(); 518 mToolbarMenuPrepared = true; 519 } 520 return result; 521 } 522 } 523 524 private final class ActionMenuPresenterCallback implements MenuPresenter.Callback { 525 private boolean mClosingActionMenu; 526 527 @Override 528 public boolean onOpenSubMenu(MenuBuilder subMenu) { 529 if (mWindowCallback != null) { 530 mWindowCallback.onMenuOpened(Window.FEATURE_ACTION_BAR, subMenu); 531 return true; 532 } 533 return false; 534 } 535 536 @Override 537 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 538 if (mClosingActionMenu) { 539 return; 540 } 541 542 mClosingActionMenu = true; 543 mToolbar.dismissPopupMenus(); 544 if (mWindowCallback != null) { 545 mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu); 546 } 547 mClosingActionMenu = false; 548 } 549 } 550 551 private final class MenuBuilderCallback implements MenuBuilder.Callback { 552 553 @Override 554 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 555 return false; 556 } 557 558 @Override 559 public void onMenuModeChange(MenuBuilder menu) { 560 if (mWindowCallback != null) { 561 if (mToolbar.isOverflowMenuShowing()) { 562 mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu); 563 } else if (mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, 564 null, menu)) { 565 mWindowCallback.onMenuOpened(Window.FEATURE_ACTION_BAR, menu); 566 } 567 } 568 } 569 } 570} 571