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 }