131f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai/* 231f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * Copyright (C) 2015 The Android Open Source Project 331f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * 431f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * Licensed under the Apache License, Version 2.0 (the "License"); 531f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * you may not use this file except in compliance with the License. 631f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * You may obtain a copy of the License at 731f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * 831f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * http://www.apache.org/licenses/LICENSE-2.0 931f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * 1031f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * Unless required by applicable law or agreed to in writing, software 1131f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * distributed under the License is distributed on an "AS IS" BASIS, 1231f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1331f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * See the License for the specific language governing permissions and 1431f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * limitations under the License 1531f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai */ 1631f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 1731f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukaipackage android.widget; 1831f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 1900aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viveretteimport android.annotation.NonNull; 2031f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukaiimport android.content.Context; 218e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasbergimport android.content.res.Configuration; 228e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasbergimport android.content.res.Resources; 238e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasbergimport android.transition.Transition; 2431f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukaiimport android.util.AttributeSet; 258e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasbergimport android.view.KeyEvent; 2600aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viveretteimport android.view.MenuItem; 2723087be7792c2d22173cf022a72d0648aa430ab5Oren Blasbergimport android.view.MotionEvent; 2831f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukaiimport android.view.View; 298e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg 308e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasbergimport com.android.internal.view.menu.ListMenuItemView; 318e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasbergimport com.android.internal.view.menu.MenuAdapter; 3223087be7792c2d22173cf022a72d0648aa430ab5Oren Blasbergimport com.android.internal.view.menu.MenuBuilder; 3331f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 3431f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai/** 3531f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * A MenuPopupWindow represents the popup window for menu. 3631f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * 3731f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * MenuPopupWindow is mostly same as ListPopupWindow, but it has customized 3831f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * behaviors specific to menus, 3931f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * 4031f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai * @hide 4131f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai */ 4223087be7792c2d22173cf022a72d0648aa430ab5Oren Blasbergpublic class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverListener { 4323087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg private MenuItemHoverListener mHoverListener; 4423087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg 4531f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 4631f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai super(context, attrs, defStyleAttr, defStyleRes); 4731f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai } 4831f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 4931f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai @Override 50f44d90b5c247f0629201d1fa322b83fa55b20608Oren Blasberg DropDownListView createDropDownListView(Context context, boolean hijackFocus) { 5123087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus); 5223087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg view.setHoverListener(this); 5323087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg return view; 5431f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai } 5531f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 568e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg public void setEnterTransition(Transition enterTransition) { 578e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg mPopup.setEnterTransition(enterTransition); 588e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg } 5931f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 6023087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg public void setExitTransition(Transition exitTransition) { 6123087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg mPopup.setExitTransition(exitTransition); 6223087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg } 6323087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg 6423087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg public void setHoverListener(MenuItemHoverListener hoverListener) { 6523087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg mHoverListener = hoverListener; 6623087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg } 6723087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg 688e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg /** 698e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg * Set whether this window is touch modal or if outside touches will be sent to 708e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg * other windows behind it. 718e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg */ 728e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg public void setTouchModal(boolean touchModal) { 738e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg mPopup.setTouchModal(touchModal); 748e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg } 7531f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 7623087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg @Override 7700aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette public void onItemHoverEnter(@NonNull MenuBuilder menu, @NonNull MenuItem item) { 7823087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg // Forward up the chain 7923087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg if (mHoverListener != null) { 8000aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette mHoverListener.onItemHoverEnter(menu, item); 8100aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette } 8200aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette } 8300aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette 8400aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette @Override 8500aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette public void onItemHoverExit(@NonNull MenuBuilder menu, @NonNull MenuItem item) { 8600aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette // Forward up the chain 8700aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (mHoverListener != null) { 8800aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette mHoverListener.onItemHoverExit(menu, item); 8923087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg } 9023087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg } 9123087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg 9223087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg /** 9323087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg * @hide 9423087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg */ 9523087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg public static class MenuDropDownListView extends DropDownListView { 968e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg final int mAdvanceKey; 978e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg final int mRetreatKey; 9831f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 9923087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg private MenuItemHoverListener mHoverListener; 10000aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette private MenuItem mHoveredMenuItem; 10123087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg 1028e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg public MenuDropDownListView(Context context, boolean hijackFocus) { 1038e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg super(context, hijackFocus); 10431f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 1058e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg final Resources res = context.getResources(); 1068e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg final Configuration config = res.getConfiguration(); 1078e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { 1088e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg mAdvanceKey = KeyEvent.KEYCODE_DPAD_LEFT; 1098e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg mRetreatKey = KeyEvent.KEYCODE_DPAD_RIGHT; 1108e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg } else { 1118e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg mAdvanceKey = KeyEvent.KEYCODE_DPAD_RIGHT; 1128e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg mRetreatKey = KeyEvent.KEYCODE_DPAD_LEFT; 11331f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai } 1148e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg } 11531f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai 11623087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg public void setHoverListener(MenuItemHoverListener hoverListener) { 11723087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg mHoverListener = hoverListener; 11823087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg } 11923087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg 12023087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg public void clearSelection() { 12123087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg setSelectedPositionInt(INVALID_POSITION); 12223087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg setNextSelectedPositionInt(INVALID_POSITION); 12323087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg } 12423087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg 1258e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg @Override 1268e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg public boolean onKeyDown(int keyCode, KeyEvent event) { 1278e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView(); 1288e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg if (selectedItem != null && keyCode == mAdvanceKey) { 12900aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (selectedItem.isEnabled() && selectedItem.getItemData().hasSubMenu()) { 1308e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg performItemClick( 1318e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg selectedItem, 1328e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg getSelectedItemPosition(), 1338e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg getSelectedItemId()); 13431f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai } 1358e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg return true; 1368e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg } else if (selectedItem != null && keyCode == mRetreatKey) { 137734320d06be5ad6365e6997b4ddbcbdb2778809cOren Blasberg setSelectedPositionInt(INVALID_POSITION); 138734320d06be5ad6365e6997b4ddbcbdb2778809cOren Blasberg setNextSelectedPositionInt(INVALID_POSITION); 1398e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg 14000aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette // Close only the top-level menu. 14100aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette ((MenuAdapter) getAdapter()).getAdapterMenu().close(false /* closeAllMenus */); 1428e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg return true; 14331f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai } 1448e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg return super.onKeyDown(keyCode, event); 14531f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai } 1468e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg 14723087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg @Override 14823087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg public boolean onHoverEvent(MotionEvent ev) { 14900aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette // Dispatch any changes in hovered item index to the listener. 15000aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (mHoverListener != null) { 15100aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette // The adapter may be wrapped. Adjust the index if necessary. 15200aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette final int headersCount; 15300aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette final MenuAdapter menuAdapter; 15400aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette final ListAdapter adapter = getAdapter(); 155ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg if (adapter instanceof HeaderViewListAdapter) { 15600aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette final HeaderViewListAdapter headerAdapter = (HeaderViewListAdapter) adapter; 15700aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette headersCount = headerAdapter.getHeadersCount(); 15800aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette menuAdapter = (MenuAdapter) headerAdapter.getWrappedAdapter(); 159ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg } else { 16000aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette headersCount = 0; 161ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg menuAdapter = (MenuAdapter) adapter; 162ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg } 163ed3912692f0ba8a647d795462e20fcdb67adbacbOren Blasberg 16400aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette // Find the menu item for the view at the event coordinates. 16500aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette MenuItem menuItem = null; 16600aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (ev.getAction() != MotionEvent.ACTION_HOVER_EXIT) { 16700aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette final int position = pointToPosition((int) ev.getX(), (int) ev.getY()); 16800aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (position != INVALID_POSITION) { 16900aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette final int itemPosition = position - headersCount; 17000aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (itemPosition >= 0 && itemPosition < menuAdapter.getCount()) { 17100aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette menuItem = menuAdapter.getItem(itemPosition); 17200aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette } 17300aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette } 17400aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette } 17500aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette 17600aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette final MenuItem oldMenuItem = mHoveredMenuItem; 17700aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (oldMenuItem != menuItem) { 17800aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette final MenuBuilder menu = menuAdapter.getAdapterMenu(); 17900aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (oldMenuItem != null) { 18000aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette mHoverListener.onItemHoverExit(menu, oldMenuItem); 18100aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette } 18200aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette 18300aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette mHoveredMenuItem = menuItem; 18400aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette 18500aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette if (menuItem != null) { 18600aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette mHoverListener.onItemHoverEnter(menu, menuItem); 18700aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette } 18800aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette } 18923087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg } 19023087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg 19100aa5103e2f71ad3f29f53168e37ef7da8ca03f2Alan Viverette return super.onHoverEvent(ev); 19223087be7792c2d22173cf022a72d0648aa430ab5Oren Blasberg } 19331f581c5a64320d9a90ce3fb1a4625f94f4f8021Jun Mukai } 1948e12f8df076d38853e0fedde7ed79e2e8689d59eOren Blasberg}