chromium/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/SuggestionAnswer.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.components.omnibox;

import android.text.TextUtils;

import androidx.annotation.VisibleForTesting;
import androidx.core.util.ObjectsCompat;

import org.jni_zero.CalledByNative;

import org.chromium.components.omnibox.AnswerTypeProto.AnswerType;

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

/** Contains the data needed to renderer a answer in the Omnibox suggestions. */
public class SuggestionAnswer {
    private final AnswerType mType;
    private final ImageLine mFirstLine;
    private final ImageLine mSecondLine;

    @VisibleForTesting
    public SuggestionAnswer(AnswerType type, ImageLine firstLine, ImageLine secondLine) {
        mType = type;
        mFirstLine = firstLine;
        mSecondLine = secondLine;
    }

    /** Return the type of Answer being shown. */
    public AnswerType getType() {
        return mType;
    }

    /** Returns the first of the two required image lines. */
    public ImageLine getFirstLine() {
        return mFirstLine;
    }

    /** Returns the second of the two required image lines. */
    public ImageLine getSecondLine() {
        return mSecondLine;
    }

    @Override
    public int hashCode() {
        // TODO(tedchoc): Replace usage of Arrays.hashCode with ObjectsCompat.hash once the support
        //                library is rolled past 27.1.
        return Arrays.hashCode(new Object[] {mType, mFirstLine, mSecondLine});
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof SuggestionAnswer)) return false;
        SuggestionAnswer other = (SuggestionAnswer) obj;
        return mType == other.mType
                && ObjectsCompat.equals(mFirstLine, other.mFirstLine)
                && ObjectsCompat.equals(mSecondLine, other.mSecondLine);
    }

    /**
     * Represents a single line of an answer, containing any number of typed text fields and an
     * optional image.
     */
    public static class ImageLine {
        private final List<TextField> mTextFields;
        private final TextField mAdditionalText;
        private final TextField mStatusText;
        private final String mImage;

        @VisibleForTesting
        public ImageLine(
                List<TextField> textFields,
                TextField additionalText,
                TextField statusText,
                String imageUrl) {
            mTextFields = textFields;
            mAdditionalText = additionalText;
            mStatusText = statusText;
            mImage = imageUrl;
        }

        /** Return an unnamed list of text fields. These represent the main content of the line. */
        public List<TextField> getTextFields() {
            return mTextFields;
        }

        /** Returns true if the line contains an "additional text" field. */
        public boolean hasAdditionalText() {
            return mAdditionalText != null;
        }

        /** Return the "additional text" field. */
        public TextField getAdditionalText() {
            return mAdditionalText;
        }

        /** Returns true if the line contains an "status text" field. */
        public boolean hasStatusText() {
            return mStatusText != null;
        }

        /** Return the "status text" field. */
        public TextField getStatusText() {
            return mStatusText;
        }

        /** Returns true if the line contains an image. */
        public boolean hasImage() {
            return mImage != null;
        }

        /** Return the optional image (URL or base64-encoded image data). */
        public String getImage() {
            return mImage;
        }

        @Override
        public int hashCode() {
            return Arrays.deepHashCode(
                    new Object[] {mTextFields.toArray(), mAdditionalText, mStatusText, mImage});
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof ImageLine)) return false;
            ImageLine other = (ImageLine) obj;

            if (mTextFields.size() != other.mTextFields.size()) return false;
            for (int i = 0; i < mTextFields.size(); i++) {
                if (!ObjectsCompat.equals(mTextFields.get(i), other.mTextFields.get(i))) {
                    return false;
                }
            }
            return TextUtils.equals(mImage, other.mImage)
                    && ObjectsCompat.equals(mAdditionalText, other.mAdditionalText)
                    && ObjectsCompat.equals(mStatusText, other.mStatusText);
        }
    }

    /** Represents one text field of an answer, containing a integer type and a string. */
    public static class TextField {
        @AnswerTextType private final int mType;
        private final String mText;
        @AnswerTextStyle private final int mStyle;
        private final int mNumLines;

        @VisibleForTesting
        public TextField(
                @AnswerTextType int type, String text, @AnswerTextStyle int style, int numLines) {
            mType = type;
            mText = text;
            mStyle = style;
            mNumLines = numLines;
        }

        @AnswerTextType
        public int getType() {
            return mType;
        }

        public String getText() {
            return mText;
        }

        @AnswerTextStyle
        public int getStyle() {
            return mStyle;
        }

        public boolean hasNumLines() {
            return mNumLines != -1;
        }

        public int getNumLines() {
            return mNumLines;
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(new Object[] {mType, mText, mStyle, mNumLines});
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof TextField)) return false;
            TextField other = (TextField) obj;
            return mType == other.mType
                    && TextUtils.equals(mText, other.mText)
                    && mStyle == other.mStyle
                    && mNumLines == other.mNumLines;
        }
    }

    @CalledByNative
    private static SuggestionAnswer createSuggestionAnswer(
            int type, ImageLine firstLine, ImageLine secondLine) {
        return new SuggestionAnswer(AnswerType.forNumber(type), firstLine, secondLine);
    }

    @CalledByNative
    private static ImageLine createImageLine(
            List<TextField> fields,
            TextField additionalText,
            TextField statusText,
            String imageUrl) {
        return new ImageLine(fields, additionalText, statusText, imageUrl);
    }

    @CalledByNative
    private static List<TextField> createTextFieldList() {
        return new ArrayList<>();
    }

    @CalledByNative
    private static void addTextFieldToList(List<TextField> list, TextField field) {
        list.add(field);
    }

    @CalledByNative
    private static TextField createTextField(
            @AnswerTextType int type, String text, @AnswerTextStyle int style, int numLines) {
        return new TextField(type, text, style, numLines);
    }
}