Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2015 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 package android.widget;
     18 
     19 import android.annotation.NonNull;
     20 import android.content.Context;
     21 import android.content.res.Configuration;
     22 import android.content.res.Resources;
     23 import android.transition.Transition;
     24 import android.util.AttributeSet;
     25 import android.view.KeyEvent;
     26 import android.view.MenuItem;
     27 import android.view.MotionEvent;
     28 import android.view.View;
     29 
     30 import com.android.internal.view.menu.ListMenuItemView;
     31 import com.android.internal.view.menu.MenuAdapter;
     32 import com.android.internal.view.menu.MenuBuilder;
     33 
     34 /**
     35  * A MenuPopupWindow represents the popup window for menu.
     36  *
     37  * MenuPopupWindow is mostly same as ListPopupWindow, but it has customized
     38  * behaviors specific to menus,
     39  *
     40  * @hide
     41  */
     42 public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverListener {
     43     private MenuItemHoverListener mHoverListener;
     44 
     45     public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     46         super(context, attrs, defStyleAttr, defStyleRes);
     47     }
     48 
     49     @Override
     50     DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
     51         MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus);
     52         view.setHoverListener(this);
     53         return view;
     54     }
     55 
     56     public void setEnterTransition(Transition enterTransition) {
     57         mPopup.setEnterTransition(enterTransition);
     58     }
     59 
     60     public void setExitTransition(Transition exitTransition) {
     61         mPopup.setExitTransition(exitTransition);
     62     }
     63 
     64     public void setHoverListener(MenuItemHoverListener hoverListener) {
     65         mHoverListener = hoverListener;
     66     }
     67 
     68     /**
     69      * Set whether this window is touch modal or if outside touches will be sent to
     70      * other windows behind it.
     71      */
     72     public void setTouchModal(boolean touchModal) {
     73         mPopup.setTouchModal(touchModal);
     74     }
     75 
     76     @Override
     77     public void onItemHoverEnter(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
     78         // Forward up the chain
     79         if (mHoverListener != null) {
     80             mHoverListener.onItemHoverEnter(menu, item);
     81         }
     82     }
     83 
     84     @Override
     85     public void onItemHoverExit(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
     86         // Forward up the chain
     87         if (mHoverListener != null) {
     88             mHoverListener.onItemHoverExit(menu, item);
     89         }
     90     }
     91 
     92     /**
     93      * @hide
     94      */
     95     public static class MenuDropDownListView extends DropDownListView {
     96         final int mAdvanceKey;
     97         final int mRetreatKey;
     98 
     99         private MenuItemHoverListener mHoverListener;
    100         private MenuItem mHoveredMenuItem;
    101 
    102         public MenuDropDownListView(Context context, boolean hijackFocus) {
    103             super(context, hijackFocus);
    104 
    105             final Resources res = context.getResources();
    106             final Configuration config = res.getConfiguration();
    107             if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
    108                 mAdvanceKey = KeyEvent.KEYCODE_DPAD_LEFT;
    109                 mRetreatKey = KeyEvent.KEYCODE_DPAD_RIGHT;
    110             } else {
    111                 mAdvanceKey = KeyEvent.KEYCODE_DPAD_RIGHT;
    112                 mRetreatKey = KeyEvent.KEYCODE_DPAD_LEFT;
    113             }
    114         }
    115 
    116         public void setHoverListener(MenuItemHoverListener hoverListener) {
    117             mHoverListener = hoverListener;
    118         }
    119 
    120         public void clearSelection() {
    121             setSelectedPositionInt(INVALID_POSITION);
    122             setNextSelectedPositionInt(INVALID_POSITION);
    123         }
    124 
    125         @Override
    126         public boolean onKeyDown(int keyCode, KeyEvent event) {
    127             ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView();
    128             if (selectedItem != null && keyCode == mAdvanceKey) {
    129                 if (selectedItem.isEnabled() && selectedItem.getItemData().hasSubMenu()) {
    130                     performItemClick(
    131                             selectedItem,
    132                             getSelectedItemPosition(),
    133                             getSelectedItemId());
    134                 }
    135                 return true;
    136             } else if (selectedItem != null && keyCode == mRetreatKey) {
    137                 setSelectedPositionInt(INVALID_POSITION);
    138                 setNextSelectedPositionInt(INVALID_POSITION);
    139 
    140                 // Close only the top-level menu.
    141                 ((MenuAdapter) getAdapter()).getAdapterMenu().close(false /* closeAllMenus */);
    142                 return true;
    143             }
    144             return super.onKeyDown(keyCode, event);
    145         }
    146 
    147         @Override
    148         public boolean onHoverEvent(MotionEvent ev) {
    149             // Dispatch any changes in hovered item index to the listener.
    150             if (mHoverListener != null) {
    151                 // The adapter may be wrapped. Adjust the index if necessary.
    152                 final int headersCount;
    153                 final MenuAdapter menuAdapter;
    154                 final ListAdapter adapter = getAdapter();
    155                 if (adapter instanceof HeaderViewListAdapter) {
    156                     final HeaderViewListAdapter headerAdapter = (HeaderViewListAdapter) adapter;
    157                     headersCount = headerAdapter.getHeadersCount();
    158                     menuAdapter = (MenuAdapter) headerAdapter.getWrappedAdapter();
    159                 } else {
    160                     headersCount = 0;
    161                     menuAdapter = (MenuAdapter) adapter;
    162                 }
    163 
    164                 // Find the menu item for the view at the event coordinates.
    165                 MenuItem menuItem = null;
    166                 if (ev.getAction() != MotionEvent.ACTION_HOVER_EXIT) {
    167                     final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
    168                     if (position != INVALID_POSITION) {
    169                         final int itemPosition = position - headersCount;
    170                         if (itemPosition >= 0 && itemPosition < menuAdapter.getCount()) {
    171                             menuItem = menuAdapter.getItem(itemPosition);
    172                         }
    173                     }
    174                 }
    175 
    176                 final MenuItem oldMenuItem = mHoveredMenuItem;
    177                 if (oldMenuItem != menuItem) {
    178                     final MenuBuilder menu = menuAdapter.getAdapterMenu();
    179                     if (oldMenuItem != null) {
    180                         mHoverListener.onItemHoverExit(menu, oldMenuItem);
    181                     }
    182 
    183                     mHoveredMenuItem = menuItem;
    184 
    185                     if (menuItem != null) {
    186                         mHoverListener.onItemHoverEnter(menu, menuItem);
    187                     }
    188                 }
    189             }
    190 
    191             return super.onHoverEvent(ev);
    192         }
    193     }
    194 }