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