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}