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