chromium/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.contextmenu;

import android.content.Context;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.SuperscriptSpan;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;

import org.chromium.chrome.R;
import org.chromium.chrome.browser.DefaultBrowserInfo;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
import org.chromium.components.browser_ui.styles.SemanticColorUtils;
import org.chromium.ui.text.SpanApplier;
import org.chromium.ui.text.SpanApplier.SpanInfo;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/** List of all predefined Context Menu Items available in Chrome. */
class ChromeContextMenuItem {
    @IntDef({
        Item.OPEN_IN_NEW_CHROME_TAB,
        Item.OPEN_IN_CHROME_INCOGNITO_TAB,
        Item.OPEN_IN_BROWSER_ID,
        Item.OPEN_IN_NEW_TAB,
        Item.OPEN_IN_INCOGNITO_TAB,
        Item.OPEN_IN_OTHER_WINDOW,
        Item.OPEN_IN_NEW_WINDOW,
        Item.OPEN_IN_EPHEMERAL_TAB,
        Item.COPY_LINK_ADDRESS,
        Item.COPY_LINK_TEXT,
        Item.SAVE_LINK_AS,
        Item.SHARE_LINK,
        Item.DIRECT_SHARE_LINK,
        Item.READ_LATER,
        Item.LOAD_ORIGINAL_IMAGE,
        Item.SAVE_IMAGE,
        Item.OPEN_IMAGE,
        Item.OPEN_IMAGE_IN_NEW_TAB,
        Item.OPEN_IMAGE_IN_EPHEMERAL_TAB,
        Item.COPY_IMAGE,
        Item.SEARCH_BY_IMAGE,
        Item.SEARCH_WITH_GOOGLE_LENS,
        Item.SHOP_IMAGE_WITH_GOOGLE_LENS,
        Item.SHARE_IMAGE,
        Item.DIRECT_SHARE_IMAGE,
        Item.CALL,
        Item.SEND_MESSAGE,
        Item.ADD_TO_CONTACTS,
        Item.COPY,
        Item.SAVE_VIDEO,
        Item.OPEN_IN_CHROME,
        Item.OPEN_IN_NEW_TAB_IN_GROUP,
        Item.SHARE_HIGHLIGHT,
        Item.REMOVE_HIGHLIGHT,
        Item.LEARN_MORE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Item {
        // Values are numerated from 0 and can't have gaps.
        // The menu and string IDs below must be kept in sync with this list.
        // Custom Tab Group
        int OPEN_IN_NEW_CHROME_TAB = 0;
        int OPEN_IN_CHROME_INCOGNITO_TAB = 1;
        int OPEN_IN_BROWSER_ID = 2;
        // Link Group
        int OPEN_IN_NEW_TAB = 3;
        int OPEN_IN_NEW_TAB_IN_GROUP = 4;
        int OPEN_IN_INCOGNITO_TAB = 5;
        int OPEN_IN_OTHER_WINDOW = 6;
        int OPEN_IN_NEW_WINDOW = 7;
        int OPEN_IN_EPHEMERAL_TAB = 8;
        int COPY_LINK_ADDRESS = 9;
        int COPY_LINK_TEXT = 10;
        int SAVE_LINK_AS = 11;
        int SHARE_LINK = 12;
        int DIRECT_SHARE_LINK = 13;
        int READ_LATER = 14;
        // Image Group
        int LOAD_ORIGINAL_IMAGE = 15;
        int SAVE_IMAGE = 16;
        int OPEN_IMAGE = 17;
        int OPEN_IMAGE_IN_NEW_TAB = 18;
        int OPEN_IMAGE_IN_EPHEMERAL_TAB = 19;
        int COPY_IMAGE = 20;
        int SEARCH_BY_IMAGE = 21;
        int SEARCH_WITH_GOOGLE_LENS = 22;
        int SHOP_IMAGE_WITH_GOOGLE_LENS = 23;
        int SHARE_IMAGE = 24;
        int DIRECT_SHARE_IMAGE = 25;
        // Message Group
        int CALL = 26;
        int SEND_MESSAGE = 27;
        int ADD_TO_CONTACTS = 28;
        int COPY = 29;
        // Video Group
        int SAVE_VIDEO = 30;
        // Other
        int OPEN_IN_CHROME = 31;
        // Shared Highlighting options
        int SHARE_HIGHLIGHT = 32;
        int REMOVE_HIGHLIGHT = 33;
        int LEARN_MORE = 34;
        // ALWAYS UPDATE!
        int NUM_ENTRIES = 35;
    }

    /** Mapping from {@link Item} to the ID found in the ids.xml. */
    private static final int[] MENU_IDS = {
        R.id.contextmenu_open_in_new_chrome_tab, // Item.OPEN_IN_NEW_CHROME_TAB
        R.id.contextmenu_open_in_chrome_incognito_tab, // Item.OPEN_IN_CHROME_INCOGNITO_TAB
        R.id.contextmenu_open_in_browser_id, // Item.OPEN_IN_BROWSER_ID
        R.id.contextmenu_open_in_new_tab, // Item.OPEN_IN_NEW_TAB
        R.id.contextmenu_open_in_new_tab_in_group, // Item.OPEN_IN_NEW_TAB_IN_GROUP
        R.id.contextmenu_open_in_incognito_tab, // Item.OPEN_IN_INCOGNITO_TAB
        R.id.contextmenu_open_in_other_window, // Item.OPEN_IN_OTHER_WINDOW
        R.id.contextmenu_open_in_new_window, // Item.OPEN_IN_NEW_WINDOW
        R.id.contextmenu_open_in_ephemeral_tab, // Item.OPEN_IN_EPHEMERAL_TAB
        R.id.contextmenu_copy_link_address, // Item.COPY_LINK_ADDRESS
        R.id.contextmenu_copy_link_text, // Item.COPY_LINK_TEXT
        R.id.contextmenu_save_link_as, // Item.SAVE_LINK_AS
        R.id.contextmenu_share_link, // Item.SHARE_LINK
        R.id.contextmenu_direct_share_link, // Item.DIRECT_SHARE_LINK
        R.id.contextmenu_read_later, // Item.READ_LATER
        R.id.contextmenu_load_original_image, // Item.LOAD_ORIGINAL_IMAGE
        R.id.contextmenu_save_image, // Item.SAVE_IMAGE
        R.id.contextmenu_open_image, // Item.OPEN_IMAGE
        R.id.contextmenu_open_image_in_new_tab, // Item.OPEN_IMAGE_IN_NEW_TAB
        R.id.contextmenu_open_image_in_ephemeral_tab, // Item.OPEN_IMAGE_IN_EPHEMERAL_TAB
        R.id.contextmenu_copy_image, // Item.COPY_IMAGE
        R.id.contextmenu_search_by_image, // Item.SEARCH_BY_IMAGE
        R.id.contextmenu_search_with_google_lens, // Item.SEARCH_WITH_GOOGLE_LENS
        R.id.contextmenu_shop_image_with_google_lens, // Item.SHOP_IMAGE_WITH_GOOGLE_LENS
        R.id.contextmenu_share_image, // Item.SHARE_IMAGE
        R.id.contextmenu_direct_share_image, // Item.DIRECT_SHARE_IMAGE
        R.id.contextmenu_call, // Item.CALL
        R.id.contextmenu_send_message, // Item.SEND_MESSAGE
        R.id.contextmenu_add_to_contacts, // Item.ADD_TO_CONTACTS
        R.id.contextmenu_copy, // Item.COPY
        R.id.contextmenu_save_video, // Item.SAVE_VIDEO
        R.id.contextmenu_open_in_chrome, // Item.OPEN_IN_CHROME
        R.id.contextmenu_share_highlight, // Item.SHARE_HIGHLIGHT
        R.id.contextmenu_remove_highlight, // Item.REMOVE_HIGHLIGHT
        R.id.contextmenu_learn_more, // Item.LEARN_MORE
    };

    /** Mapping from {@link Item} to the ID of the string that describes the action of the item. */
    private static final int[] STRING_IDS = {
        R.string.contextmenu_open_in_new_chrome_tab, // Item.OPEN_IN_NEW_CHROME_TAB:
        R.string.contextmenu_open_in_chrome_incognito_tab, // Item.OPEN_IN_CHROME_INCOGNITO_TAB:
        0, // Item.OPEN_IN_BROWSER_ID is not handled by this mapping.
        R.string.contextmenu_open_in_new_tab, // Item.OPEN_IN_NEW_TAB:
        R.string.contextmenu_open_in_new_tab_group, // Item.OPEN_IN_NEW_TAB_IN_GROUP
        R.string.contextmenu_open_in_incognito_tab, // Item.OPEN_IN_INCOGNITO_TAB:
        R.string.contextmenu_open_in_other_window, // Item.OPEN_IN_OTHER_WINDOW:
        R.string.contextmenu_open_in_new_window, // Item.OPEN_IN_NEW_WINDOW:
        R.string.contextmenu_open_in_ephemeral_tab, // Item.OPEN_IN_EPHEMERAL_TAB:
        R.string.contextmenu_copy_link_address, // Item.COPY_LINK_ADDRESS:
        R.string.contextmenu_copy_link_text, // Item.COPY_LINK_TEXT:
        R.string.contextmenu_save_link, // Item.SAVE_LINK_AS:
        R.string.contextmenu_share_link, // Item.SHARE_LINK
        0, // Item.DIRECT_SHARE_LINK is not handled by this mapping.
        R.string.contextmenu_read_later, // Item.READ_LATER
        R.string.contextmenu_load_original_image, // Item.LOAD_ORIGINAL_IMAGE:
        R.string.contextmenu_save_image, // Item.SAVE_IMAGE:
        R.string.contextmenu_open_image, // Item.OPEN_IMAGE:
        R.string.contextmenu_open_image_in_new_tab, // Item.OPEN_IMAGE_IN_NEW_TAB:
        R.string.contextmenu_open_image_in_ephemeral_tab, // Item.OPEN_IMAGE_IN_EPHEMERAL_TAB:
        R.string.contextmenu_copy_image, // Item.COPY_IMAGE:
        R.string.contextmenu_search_web_for_image, // Item.SEARCH_BY_IMAGE:
        R.string.contextmenu_search_image_with_google_lens, // Item.SEARCH_WITH_GOOGLE_LENS:
        R.string.contextmenu_shop_image_with_google_lens, // Item.SHOP_IMAGE_WITH_GOOGLE_LENS:
        R.string.contextmenu_share_image, // Item.SHARE_IMAGE
        0, // Item.DIRECT_SHARE_IMAGE is not handled by this mapping.
        R.string.contextmenu_call, // Item.CALL:
        R.string.contextmenu_send_message, // Item.SEND_MESSAGE:
        R.string.contextmenu_add_to_contacts, // Item.ADD_TO_CONTACTS:
        R.string.contextmenu_copy, // Item.COPY:
        R.string.contextmenu_save_video, // Item.SAVE_VIDEO:
        R.string.menu_open_in_chrome, // Item.OPEN_IN_CHROME:
        R.string.contextmenu_share_highlight, // Item.SHARE_HIGHLIGHT
        R.string.contextmenu_remove_highlight, // Item.REMOVE_HIGHLIGHT
        R.string.contextmenu_learn_more, // Item.LEARN_MORE
    };

    /**
     * Returns the menu id for a given {@link @Item}.
     * @param item The {@link @Item}.
     * @return Menu id associated with the {@code item}.
     */
    public static int getMenuId(@Item int item) {
        assert MENU_IDS.length == Item.NUM_ENTRIES;
        return MENU_IDS[item];
    }

    /**
     * Get string ID from the ID of the item.
     * @param context The activity context.
     * @param item #Item Item ID.
     * @return Returns the string that describes the action of the item.
     */
    private static @StringRes int getStringId(Context context, @Item int item) {
        assert STRING_IDS.length == Item.NUM_ENTRIES;

        return STRING_IDS[item];
    }

    /**
     * Transforms the id of the item into a string. It manages special cases that need minor changes
     * due to templating.
     *
     * @param context Requires to get the string resource related to the item.
     * @param profile The {@link Profile} associated with the current page.
     * @param item Context menu item id.
     * @param showInProductHelp Whether the menu item should show the new superscript label.
     * @return Returns a string for the menu item.
     */
    public static CharSequence getTitle(
            Context context, Profile profile, @Item int item, boolean showInProductHelp) {
        switch (item) {
            case Item.OPEN_IN_BROWSER_ID:
                return DefaultBrowserInfo.getTitleOpenInDefaultBrowser(false);
            case Item.SEARCH_BY_IMAGE:
                return context.getString(
                        getStringId(context, item),
                        TemplateUrlServiceFactory.getForProfile(profile)
                                .getDefaultSearchEngineTemplateUrl()
                                .getShortName());
            case Item.READ_LATER:
                return addOrRemoveNewLabel(context, item, null, showInProductHelp);
            case Item.OPEN_IN_EPHEMERAL_TAB:
                return addOrRemoveNewLabel(
                        context,
                        item,
                        ChromePreferenceKeys.CONTEXT_MENU_OPEN_IN_EPHEMERAL_TAB_CLICKED,
                        showInProductHelp);
            case Item.OPEN_IMAGE_IN_EPHEMERAL_TAB:
                return addOrRemoveNewLabel(
                        context,
                        item,
                        ChromePreferenceKeys.CONTEXT_MENU_OPEN_IMAGE_IN_EPHEMERAL_TAB_CLICKED,
                        showInProductHelp);
            case Item.SEARCH_WITH_GOOGLE_LENS:
                return addOrRemoveNewLabel(
                        context,
                        item,
                        ChromePreferenceKeys.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS_CLICKED,
                        showInProductHelp);
            case Item.SHOP_IMAGE_WITH_GOOGLE_LENS:
                return addOrRemoveNewLabel(
                        context,
                        item,
                        ChromePreferenceKeys.CONTEXT_MENU_SHOP_IMAGE_WITH_GOOGLE_LENS_CLICKED,
                        showInProductHelp);
            default:
                return context.getString(getStringId(context, item));
        }
    }

    /**
     * Modify the menu title by applying span attributes or removing the 'New' label if the menu
     * has already been selected before.
     */
    private static CharSequence addOrRemoveNewLabel(
            Context context, @Item int item, @Nullable String prefKey, boolean showNewLabel) {
        String menuTitle = context.getString(getStringId(context, item));
        if (!showNewLabel
                || (prefKey != null
                        && ChromeSharedPreferences.getInstance().readBoolean(prefKey, false))) {
            return SpanApplier.removeSpanText(menuTitle, new SpanInfo("<new>", "</new>"));
        }
        return SpanApplier.applySpans(
                menuTitle,
                new SpanInfo(
                        "<new>",
                        "</new>",
                        new SuperscriptSpan(),
                        new RelativeSizeSpan(0.75f),
                        new ForegroundColorSpan(
                                SemanticColorUtils.getDefaultTextColorAccent1(context))));
    }
}