chromium/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesList.java

// Copyright 2021 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.contextualsearch;

import android.net.Uri;
import android.text.TextUtils;

import androidx.annotation.Nullable;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import org.chromium.base.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * A list of Related Searches built from JSON that represents the suggestions that we show in the
 * UI.
 */
class RelatedSearchesList {
    private static final String TAG = "ContextualSearch";

    /** JSON keys sent by the server. */
    private static final String SELECTION_SUGGESTIONS = "selection";

    private static final String TITLE = "title";
    private static final String SEARCH_URL = "searchUrl";

    /** The parsed Json suggestions. */
    private final JSONObject mJsonSuggestions;

    /**
     * Constructs an instance from a JSON string.
     * @param jsonString The JSON string for all the suggestions typically returned by the server,
     *        or an empty or {@code null} string.
     */
    RelatedSearchesList(@Nullable String jsonString) {
        JSONObject suggestions = new JSONObject();
        if (!TextUtils.isEmpty(jsonString)) {
            try {
                suggestions = new JSONObject(jsonString);
            } catch (JSONException e) {
                Log.w(
                        TAG,
                        "RelatedSearchesList cannot parse JSON: "
                                + jsonString
                                + "\n"
                                + e.getMessage());
            }
        }
        mJsonSuggestions = suggestions;
    }

    /**
     * Returns a list of queries. This implementation may change based on whether we're showing
     * suggestions in more than one place or not. This just returns the "default" list with
     * the current interpretation of that concept.
     * @return A {@code List<String>} of search suggestions.
     */
    List<String> getQueries() {
        List<String> results = new ArrayList<String>();
        JSONArray suggestions = getSuggestions();
        if (suggestions == null) return results;
        for (int i = 0; i < suggestions.length(); i++) {
            try {
                results.add(suggestions.getJSONObject(i).getString(TITLE));
            } catch (JSONException e) {
                Log.w(
                        TAG,
                        "RelatedSearchesList cannot find a query with a title at suggestion "
                                + "index: "
                                + i
                                + "\n"
                                + e.getMessage());
            }
        }
        return results;
    }

    /**
     * Returns the URI for the search request for the given suggestion.
     * @param suggestionIndex Which suggestion to get, zero-based from the list sent by the server.
     * @return A URI that can be used to load the SERP in the Panel, or {@code null} in case of an
     *         error.
     */
    @Nullable
    Uri getSearchUri(int suggestionIndex) {
        JSONArray suggestions = getSuggestions();
        if (suggestions == null) return null;
        try {
            String searchUrl = suggestions.getJSONObject(suggestionIndex).getString(SEARCH_URL);
            Uri searchUri = Uri.parse(searchUrl);
            return RelatedSearchesStamp.updateUriForSuggestionPosition(searchUri, suggestionIndex);
        } catch (JSONException e) {
            Log.w(
                    TAG,
                    "RelatedSearchesList cannot find a searchUrl in suggestion "
                            + suggestionIndex
                            + "\n"
                            + e.getMessage());
        }
        return null;
    }

    /**
     * Returns the suggestions array to show in the panel, or {@code null} if none.
     * @return A {@link JSONArray} of suggestions, or {@code null} in case of an error.
     */
    @Nullable
    JSONArray getSuggestions() {
        try {
            return mJsonSuggestions.getJSONArray(SELECTION_SUGGESTIONS);
        } catch (JSONException e) {
            Log.w(TAG, "No suggestions found!\n" + e.getMessage());
            return null;
        }
    }
}