chromium/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java

// Copyright 2018 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.omnibox.suggestions.editurl;

import android.content.Context;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.history_clusters.HistoryClustersTabHelper;
import org.chromium.chrome.browser.omnibox.R;
import org.chromium.chrome.browser.omnibox.styles.OmniboxDrawableState;
import org.chromium.chrome.browser.omnibox.styles.OmniboxImageSupplier;
import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
import org.chromium.chrome.browser.omnibox.styles.SuggestionSpannable;
import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties.Action;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties;
import org.chromium.chrome.browser.share.ShareDelegate;
import org.chromium.chrome.browser.share.ShareDelegate.ShareOrigin;
import org.chromium.chrome.browser.tab.SadTab;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.omnibox.AutocompleteMatch;
import org.chromium.components.omnibox.OmniboxSuggestionType;
import org.chromium.components.omnibox.suggestions.OmniboxSuggestionUiType;
import org.chromium.components.ukm.UkmRecorder;
import org.chromium.ui.base.Clipboard;
import org.chromium.ui.modelutil.PropertyModel;

import java.util.Arrays;
import java.util.Optional;

/**
 * This class controls the interaction of the "edit url" suggestion item with the rest of the
 * suggestions list. This class also serves as a mediator, containing logic that interacts with the
 * rest of Chrome.
 */
public class EditUrlSuggestionProcessor extends BaseSuggestionViewProcessor {
    private final @NonNull Supplier<ShareDelegate> mShareDelegateSupplier;
    private final @NonNull Supplier<Tab> mTabSupplier;

    public EditUrlSuggestionProcessor(
            Context context,
            SuggestionHost suggestionHost,
            Optional<OmniboxImageSupplier> imageSupplier,
            Supplier<Tab> tabSupplier,
            Supplier<ShareDelegate> shareDelegateSupplier) {
        super(context, suggestionHost, imageSupplier);

        mTabSupplier = tabSupplier;
        mShareDelegateSupplier = shareDelegateSupplier;
    }

    @Override
    public boolean doesProcessSuggestion(@NonNull AutocompleteMatch suggestion, int position) {
        // The what-you-typed suggestion can potentially appear as the second suggestion in some
        // cases. If the first suggestion isn't the one we want, ignore all subsequent suggestions.
        if (position != 0) return false;

        Tab activeTab = mTabSupplier.get();
        if (activeTab == null
                || !activeTab.isInitialized()
                || activeTab.isNativePage()
                || SadTab.isShowing(activeTab)) {
            return false;
        }

        if (suggestion.getType() != OmniboxSuggestionType.URL_WHAT_YOU_TYPED
                || !suggestion.getUrl().equals(activeTab.getUrl())) {
            return false;
        }

        return true;
    }

    @Override
    public int getViewTypeId() {
        return OmniboxSuggestionUiType.EDIT_URL_SUGGESTION;
    }

    @Override
    public @NonNull PropertyModel createModel() {
        return new PropertyModel(SuggestionViewProperties.ALL_KEYS);
    }

    @Override
    public void populateModel(
            @NonNull AutocompleteMatch suggestion, @NonNull PropertyModel model, int position) {
        super.populateModel(suggestion, model, position);

        var tab = mTabSupplier.get();
        var title = suggestion.getDescription();
        if (!tab.isLoading()) {
            title = tab.getTitle();
        } else if (TextUtils.isEmpty(title)) {
            title = mContext.getResources().getText(R.string.tab_loading_default_title).toString();
        }

        model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT, new SuggestionSpannable(title));
        model.set(
                SuggestionViewProperties.TEXT_LINE_2_TEXT,
                new SuggestionSpannable(suggestion.getDisplayText()));

        setActionButtons(
                model,
                Arrays.asList(
                        new Action(
                                OmniboxDrawableState.forSmallIcon(
                                        mContext, R.drawable.ic_share_white_24dp, true),
                                OmniboxResourceProvider.getString(
                                        mContext, R.string.menu_share_page),
                                null,
                                this::onShareLink),
                        new Action(
                                OmniboxDrawableState.forSmallIcon(
                                        mContext, R.drawable.ic_content_copy_black, true),
                                OmniboxResourceProvider.getString(mContext, R.string.copy_link),
                                () -> onCopyLink(suggestion)),
                        // TODO(crbug.com/40697047): do not re-use bookmark_item_edit here.
                        new Action(
                                OmniboxDrawableState.forSmallIcon(
                                        mContext, R.drawable.bookmark_edit_active, true),
                                OmniboxResourceProvider.getString(
                                        mContext, R.string.bookmark_item_edit),
                                () -> onEditLink(suggestion))));

        fetchSuggestionFavicon(model, suggestion.getUrl());
    }

    @Override
    protected void onSuggestionClicked(@NonNull AutocompleteMatch suggestion, int position) {
        RecordUserAction.record("Omnibox.EditUrlSuggestion.Tap");
        super.onSuggestionClicked(suggestion, position);
    }

    /** Invoked when user interacts with Share action button. */
    private void onShareLink() {
        RecordUserAction.record("Omnibox.EditUrlSuggestion.Share");
        var webContents = mTabSupplier.get().getWebContents();
        if (webContents != null) {
            // TODO(ender): find out if this is still captured anywhere.
            new UkmRecorder.Bridge()
                    .recordEventWithBooleanMetric(
                            webContents, "Omnibox.EditUrlSuggestion.Share", "HasOccurred");
        }
        mSuggestionHost.finishInteraction();
        // TODO(mdjones): This should only share the displayed URL instead of the background tab.
        mShareDelegateSupplier.get().share(mTabSupplier.get(), false, ShareOrigin.EDIT_URL);
    }

    /** Invoked when user interacts with Copy action button. */
    private void onCopyLink(AutocompleteMatch suggestion) {
        RecordUserAction.record("Omnibox.EditUrlSuggestion.Copy");
        HistoryClustersTabHelper.onCurrentTabUrlCopied(mTabSupplier.get().getWebContents());
        Clipboard.getInstance().copyUrlToClipboard(suggestion.getUrl());
    }

    /** Invoked when user interacts with Edit action button. */
    private void onEditLink(AutocompleteMatch suggestion) {
        RecordUserAction.record("Omnibox.EditUrlSuggestion.Edit");
        mSuggestionHost.onRefineSuggestion(suggestion);
    }
}