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