chromium/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.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.searchwidget;

import android.content.Context;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import org.chromium.chrome.R;
import org.chromium.chrome.browser.lens.LensEntryPoint;
import org.chromium.chrome.browser.locale.LocaleManager;
import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
import org.chromium.chrome.browser.omnibox.LocationBarLayout;
import org.chromium.chrome.browser.omnibox.UrlBar;
import org.chromium.chrome.browser.omnibox.UrlBarCoordinator;
import org.chromium.chrome.browser.omnibox.UrlBarCoordinator.SelectionState;
import org.chromium.chrome.browser.omnibox.UrlBarData;
import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
import org.chromium.chrome.browser.toolbar.top.ToolbarPhone;
import org.chromium.chrome.browser.ui.searchactivityutils.SearchActivityExtras.IntentOrigin;
import org.chromium.chrome.browser.ui.searchactivityutils.SearchActivityExtras.SearchType;
import org.chromium.chrome.browser.ui.searchactivityutils.SearchActivityPreferencesManager;
import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.widget.Toast;

/** Implementation of the {@link LocationBarLayout} that is displayed for widget searches. */
public class SearchActivityLocationBarLayout extends LocationBarLayout {
    private boolean mPendingSearchPromoDecision;
    private boolean mPendingBeginQuery;
    private boolean mInteractionFromWidget;

    public SearchActivityLocationBarLayout(Context context, AttributeSet attrs) {
        super(context, attrs, R.layout.location_bar_base);
    }

    @Override
    public void initialize(
            @NonNull AutocompleteCoordinator autocompleteCoordinator,
            @NonNull UrlBarCoordinator urlCoordinator,
            @NonNull StatusCoordinator statusCoordinator,
            @NonNull LocationBarDataProvider locationBarDataProvider) {
        super.initialize(
                autocompleteCoordinator,
                urlCoordinator,
                statusCoordinator,
                locationBarDataProvider);
        mPendingSearchPromoDecision = LocaleManager.getInstance().needToCheckForSearchEnginePromo();
        mAutocompleteCoordinator.setShouldPreventOmniboxAutocomplete(mPendingSearchPromoDecision);
        findViewById(R.id.url_action_container).setVisibility(View.VISIBLE);

        GradientDrawable backgroundDrawable =
                ToolbarPhone.createModernLocationBarBackground(getContext());
        backgroundDrawable.setTint(
                ChromeColors.getSurfaceColor(
                        getContext(), R.dimen.omnibox_suggestion_bg_elevation));
        backgroundDrawable.setCornerRadius(
                getResources()
                        .getDimensionPixelSize(R.dimen.omnibox_suggestion_bg_round_corner_radius));

        setBackground(backgroundDrawable);

        // Expand status view's left and right space, and expand the vertical padding of the
        // location bar to match the expanded interface on the regular omnibox.
        setUrlFocusChangePercent(1f, 1f, /* isUrlFocusChangeInProgress= */ false);
    }

    @Override
    public void onFinishNativeInitialization() {
        super.onFinishNativeInitialization();

        mPendingSearchPromoDecision = LocaleManager.getInstance().needToCheckForSearchEnginePromo();
        mAutocompleteCoordinator.setShouldPreventOmniboxAutocomplete(mPendingSearchPromoDecision);
    }

    /** Called when the SearchActivity has finished initialization. */
    void onDeferredStartup(@SearchType int searchType, @NonNull WindowAndroid windowAndroid) {
        SearchActivityPreferencesManager.updateFeatureAvailability(getContext(), windowAndroid);
        assert !LocaleManager.getInstance().needToCheckForSearchEnginePromo();
        mPendingSearchPromoDecision = false;
        mAutocompleteCoordinator.setShouldPreventOmniboxAutocomplete(mPendingSearchPromoDecision);
        // Do not prefetch suggestions here; instead, we're asking the server for ZPS directly.
        // Issuing multiple requests would result with only the final one being executed.
        mAutocompleteCoordinator.onTextChanged(mUrlCoordinator.getTextWithoutAutocomplete());

        if (mPendingBeginQuery) {
            beginQueryInternal(searchType, windowAndroid);
            mPendingBeginQuery = false;
        }
    }

    /**
     * Begins a new query.
     *
     * @param origin The SearchActivity requestor.
     * @param searchType The type of search to invoke.
     * @param optionalText Prepopulate with a query, this may be null.
     * @param windowAndroid WindowAndroid context.
     */
    @VisibleForTesting
    public void beginQuery(
            @IntentOrigin int origin,
            @SearchType int searchType,
            @Nullable String optionalText,
            @NonNull WindowAndroid windowAndroid) {

        if (origin == IntentOrigin.CUSTOM_TAB) {
            mUrlBar.setHint(R.string.omnibox_on_cct_empty_hint);
        } else {
            mUrlBar.setHint(R.string.omnibox_empty_hint);
        }
        // Clear the text regardless of the promo decision.  This allows the user to enter text
        // before native has been initialized and have it not be cleared one the delayed beginQuery
        // logic is performed.
        mUrlCoordinator.setUrlBarData(
                UrlBarData.forNonUrlText(optionalText == null ? "" : optionalText),
                UrlBar.ScrollType.NO_SCROLL,
                SelectionState.SELECT_ALL);

        if (mPendingSearchPromoDecision || (searchType != SearchType.TEXT && !mNativeInitialized)) {
            mPendingBeginQuery = true;
            return;
        }

        beginQueryInternal(searchType, windowAndroid);
    }

    private void beginQueryInternal(
            @SearchType int searchType, @NonNull WindowAndroid windowAndroid) {
        assert !mPendingSearchPromoDecision;

        // Update voice and lens eligibility in case anything changed in the process.
        if (mNativeInitialized) {
            SearchActivityPreferencesManager.updateFeatureAvailability(getContext(), windowAndroid);
        }

        mInteractionFromWidget = true;
        if (searchType == SearchType.VOICE) {
            runVoiceSearch();
        } else if (searchType == SearchType.LENS) {
            runGoogleLens();
        }
        requestOmniboxFocus();
        mInteractionFromWidget = false;
    }

    /** Begins a new Voice query. */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    void runVoiceSearch() {
        View micButton = findViewById(R.id.mic_button);
        if (!micButton.performClick()) {
            // Voice recognition is not available. Fall back to regular text search.
            Toast.makeText(
                            getContext(),
                            R.string.quick_action_search_widget_message_no_voice_search,
                            Toast.LENGTH_LONG)
                    .show();
        }
    }

    /** Begins a new Lens query. */
    private void runGoogleLens() {
        View lensButton = findViewById(R.id.lens_camera_button);
        if (!lensButton.performClick()) {
            Toast.makeText(
                            getContext(),
                            R.string.quick_action_search_widget_message_no_google_lens,
                            Toast.LENGTH_LONG)
                    .show();
        }
    }

    /** Focus the Omnibox and present the cached suggestions. */
    void requestOmniboxFocus() {
        mUrlBar.post(
                () -> {
                    if (mUrlCoordinator == null || mAutocompleteCoordinator == null) {
                        return;
                    }

                    mUrlBar.requestFocus();
                    mUrlCoordinator.setKeyboardVisibility(true, false);
                });
    }

    void clearOmniboxFocus() {
        mUrlBar.post(() -> mUrlBar.clearFocus());
    }

    @Override
    public int getVoiceRecogintionSource() {
        return mInteractionFromWidget
                ? VoiceRecognitionHandler.VoiceInteractionSource.SEARCH_WIDGET
                : super.getVoiceRecogintionSource();
    }

    @Override
    public int getLensEntryPoint() {
        return mInteractionFromWidget
                ? LensEntryPoint.QUICK_ACTION_SEARCH_WIDGET
                : super.getLensEntryPoint();
    }
}