AppMenuButtonHelper.java revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.chrome.browser.appmenu;
6
7import android.view.GestureDetector;
8import android.view.GestureDetector.SimpleOnGestureListener;
9import android.view.MotionEvent;
10import android.view.View;
11import android.view.View.OnTouchListener;
12
13import org.chromium.chrome.browser.UmaBridge;
14
15/**
16 * A helper class for a menu button to decide when to show the app menu and forward touch
17 * events.
18 *
19 * Simply construct this class and pass the class instance to a menu button as TouchListener.
20 * Then this class will handle everything regarding showing app menu for you.
21 */
22public class AppMenuButtonHelper extends SimpleOnGestureListener implements OnTouchListener {
23    private final View mMenuButton;
24    private final AppMenuHandler mMenuHandler;
25    private final GestureDetector mAppMenuGestureDetector;
26    private boolean mSeenFirstScrollEvent;
27    private Runnable mOnAppMenuShownListener;
28
29    /**
30     * @param menuButton  Menu button instance that will trigger the app menu.
31     * @param menuHandler MenuHandler implementation that can show and get the app menu.
32     */
33    public AppMenuButtonHelper(View menuButton, AppMenuHandler menuHandler) {
34        mMenuButton = menuButton;
35        mMenuHandler = menuHandler;
36        mAppMenuGestureDetector = new GestureDetector(menuButton.getContext(), this);
37        mAppMenuGestureDetector.setIsLongpressEnabled(false);
38    }
39
40    /**
41     * @param onAppMenuShownListener This is called when the app menu is shown by this class.
42     */
43    public void setOnAppMenuShownListener(Runnable onAppMenuShownListener) {
44        mOnAppMenuShownListener = onAppMenuShownListener;
45    }
46
47    /**
48     * Shows the app menu if it is not already shown.
49     * @param startDragging Whether dragging is started.
50     * @return Whether or not if the app menu is successfully shown.
51     */
52    private boolean showAppMenu(boolean startDragging) {
53        if (!mMenuHandler.isAppMenuShowing() &&
54                mMenuHandler.showAppMenu(mMenuButton, false, startDragging)) {
55            UmaBridge.usingMenu(false, startDragging);
56            if (mOnAppMenuShownListener != null) {
57                mOnAppMenuShownListener.run();
58            }
59            return true;
60        }
61        return false;
62    }
63
64    /**
65     * @return Whether the App Menu is currently showing.
66     */
67    public boolean isAppMenuShowing() {
68        return mMenuHandler.isAppMenuShowing();
69    }
70
71    /**
72     * Handle the key press event on a menu button.
73     * @return Whether the app menu was shown as a result of this action.
74     */
75    public boolean onEnterKeyPress() {
76        return showAppMenu(false);
77    }
78
79    @Override
80    public boolean onDown(MotionEvent e) {
81        mSeenFirstScrollEvent = false;
82        mMenuButton.setPressed(true);
83
84        return true;
85    }
86
87    @Override
88    public boolean onSingleTapUp(MotionEvent e) {
89        // A single tap shows the app menu, but dragging disabled.
90        return showAppMenu(false);
91    }
92
93    @Override
94    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
95        if (mSeenFirstScrollEvent) return false;
96        mSeenFirstScrollEvent = true;
97
98        // If the scrolling direction is roughly down on the first onScroll detection,
99        // we consider it as dragging start, so shows the app menu. Otherwise, we
100        // don't show menu so that toolbar horizontal swiping can happen.
101        return -distanceY >= Math.abs(distanceX) && showAppMenu(true);
102    }
103
104    @Override
105    public boolean onTouch(View view, MotionEvent event) {
106        if (event.getActionMasked() == MotionEvent.ACTION_CANCEL ||
107                event.getActionMasked() == MotionEvent.ACTION_UP) {
108            mMenuButton.setPressed(false);
109        }
110
111        // This will take care of showing app menu.
112        boolean isTouchEventConsumed = mAppMenuGestureDetector.onTouchEvent(event);
113
114        // If user starts to drag on this menu button, ACTION_DOWN and all the subsequent touch
115        // events are received here. We need to forward this event to the app menu to handle
116        // dragging correctly.
117        AppMenu menu = mMenuHandler.getAppMenu();
118        if (menu != null && !isTouchEventConsumed) {
119            isTouchEventConsumed |= menu.handleDragging(event);
120        }
121        return isTouchEventConsumed;
122    }
123}