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