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.contextmenu;
6
7import android.view.ContextMenu;
8import android.view.ContextMenu.ContextMenuInfo;
9import android.view.HapticFeedbackConstants;
10import android.view.Menu;
11import android.view.MenuItem;
12import android.view.MenuItem.OnMenuItemClickListener;
13import android.view.View;
14import android.view.View.OnCreateContextMenuListener;
15
16import org.chromium.base.CalledByNative;
17import org.chromium.base.VisibleForTesting;
18import org.chromium.content.browser.ContentViewCore;
19
20/**
21 * A helper class that handles generating context menus for {@link ContentViewCore}s.
22 */
23public class ContextMenuHelper implements OnCreateContextMenuListener, OnMenuItemClickListener {
24    private long mNativeContextMenuHelper;
25
26    private ContextMenuPopulator mPopulator;
27    private ContextMenuParams mCurrentContextMenuParams;
28
29    private ContextMenuHelper(long nativeContextMenuHelper) {
30        mNativeContextMenuHelper = nativeContextMenuHelper;
31    }
32
33    @CalledByNative
34    private static ContextMenuHelper create(long nativeContextMenuHelper) {
35        return new ContextMenuHelper(nativeContextMenuHelper);
36    }
37
38    @CalledByNative
39    private void destroy() {
40        mNativeContextMenuHelper = 0;
41    }
42
43    /**
44     * @param populator A {@link ContextMenuPopulator} that is responsible for managing and showing
45     *                  context menus.
46     */
47    @CalledByNative
48    private void setPopulator(ContextMenuPopulator populator) {
49        mPopulator = populator;
50    }
51
52    /**
53     * Starts showing a context menu for {@code view} based on {@code params}.
54     * @param contentViewCore The {@link ContentViewCore} to show the menu to.
55     * @param params          The {@link ContextMenuParams} that indicate what menu items to show.
56     */
57    @CalledByNative
58    private void showContextMenu(ContentViewCore contentViewCore, ContextMenuParams params) {
59        final View view = contentViewCore.getContainerView();
60
61        if (!shouldShowMenu(params)
62                || view == null
63                || view.getVisibility() != View.VISIBLE
64                || view.getParent() == null) {
65            return;
66        }
67
68        mCurrentContextMenuParams = params;
69
70        view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
71        contentViewCore.setIgnoreRemainingTouchEvents();
72        view.setOnCreateContextMenuListener(this);
73        view.showContextMenu();
74    }
75
76    /**
77     * Starts a download based on the current {@link ContextMenuParams}.
78     * @param isLink Whether or not the download target is a link.
79     */
80    public void startContextMenuDownload(boolean isLink) {
81        if (mNativeContextMenuHelper != 0) nativeOnStartDownload(mNativeContextMenuHelper, isLink);
82    }
83
84    @Override
85    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
86        if (!shouldShowMenu(mCurrentContextMenuParams)) return;
87
88        if (mCurrentContextMenuParams.isCustomMenu()) {
89            for (int i = 0; i < mCurrentContextMenuParams.getCustomMenuSize(); i++) {
90                menu.add(Menu.NONE, i, Menu.NONE, mCurrentContextMenuParams.getCustomLabelAt(i));
91            }
92        } else {
93            assert mPopulator != null;
94            mPopulator.buildContextMenu(menu, v.getContext(), mCurrentContextMenuParams);
95        }
96
97        for (int i = 0; i < menu.size(); i++) {
98            menu.getItem(i).setOnMenuItemClickListener(this);
99        }
100    }
101
102    @Override
103    public boolean onMenuItemClick(MenuItem item) {
104        if (mCurrentContextMenuParams.isCustomMenu()) {
105            if (mNativeContextMenuHelper != 0) {
106                final int action = mCurrentContextMenuParams.getCustomActionAt(item.getItemId());
107                nativeOnCustomItemSelected(mNativeContextMenuHelper, action);
108            }
109            return true;
110        } else {
111            return mPopulator.onItemSelected(this, mCurrentContextMenuParams, item.getItemId());
112        }
113    }
114
115    /**
116     * @return The {@link ContextMenuPopulator} responsible for populating the context menu.
117     */
118    @VisibleForTesting
119    public ContextMenuPopulator getPopulator() {
120        return mPopulator;
121    }
122
123    private boolean shouldShowMenu(ContextMenuParams params) {
124        // Custom menus are handled by this class and do not require a ContextMenuPopulator.
125        return params.isCustomMenu() ||
126                (mPopulator != null && mPopulator.shouldShowContextMenu(params));
127    }
128
129    private native void nativeOnStartDownload(long nativeContextMenuHelper, boolean isLink);
130    private native void nativeOnCustomItemSelected(long nativeContextMenuHelper, int action);
131}