chromium/components/android_autofill/browser/java/src/org/chromium/components/autofill/FormFieldData.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.components.autofill;

import android.graphics.RectF;
import android.view.autofill.AutofillId;

import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;

import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;

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

/** The wrap class of native autofill::FormFieldDataAndroid. */
@JNINamespace("autofill")
public class FormFieldData {
    /**
     * Define the control types supported by android.view.autofill.AutofillValue.
     *
     * Android doesn't have DATALIST control, it is sent to the Autofill service as
     * View.AUTOFILL_TYPE_TEXT with AutofillOptions.
     */
    @IntDef({ControlType.TEXT, ControlType.TOGGLE, ControlType.LIST, ControlType.DATALIST})
    @Retention(RetentionPolicy.SOURCE)
    public @interface ControlType {
        int TEXT = 0;
        int TOGGLE = 1;
        int LIST = 2;
        int DATALIST = 3;
    }

    public final String mLabel;
    public final String mName;
    public final String mAutocompleteAttr;
    public final boolean mShouldAutocomplete;
    public final String mPlaceholder;
    public final String mType;
    public final String mId;
    public final String[] mOptionValues;
    public final String[] mOptionContents;
    public final @ControlType int mControlType;
    public final int mMaxLength;
    public final String mHeuristicType;
    public final String[] mDatalistValues;
    public final String[] mDatalistLabels;

    // The bounds in the viewport's coordinates
    private RectF mBounds;
    // The bounds in the container view's coordinates.
    private RectF mBoundsInContainerViewCoordinates;

    private boolean mIsChecked;
    private String mValue;
    private boolean mVisible;
    // Indicates whether mValue is autofilled.
    private boolean mAutofilled;
    // Indicates whether this fields was autofilled, but changed by user.
    private boolean mPreviouslyAutofilled;

    // Provides the field type along with mHeuristicType, but could be changed
    // after the object instantiated.
    private String mServerType;
    private String mComputedType;
    private String[] mServerPredictions;
    private AutofillId mAutofillId;

    private FormFieldData(
            String name,
            String label,
            String value,
            String autocompleteAttr,
            boolean shouldAutocomplete,
            String placeholder,
            String type,
            String id,
            String[] optionValues,
            String[] optionContents,
            boolean isCheckField,
            boolean isChecked,
            int maxLength,
            String heuristicType,
            String serverType,
            String computedType,
            String[] serverPredictions,
            float left,
            float top,
            float right,
            float bottom,
            String[] datalistValues,
            String[] datalistLabels,
            boolean visible,
            boolean isAutofilled) {
        mName = name;
        mLabel = label;
        mValue = value;
        mAutocompleteAttr = autocompleteAttr;
        mShouldAutocomplete = shouldAutocomplete;
        mPlaceholder = placeholder;
        mType = type;
        mId = id;
        mOptionValues = optionValues;
        mOptionContents = optionContents;
        mIsChecked = isChecked;
        mDatalistLabels = datalistLabels;
        mDatalistValues = datalistValues;
        if (mOptionValues != null && mOptionValues.length != 0) {
            mControlType = ControlType.LIST;
        } else if (mDatalistValues != null && mDatalistValues.length != 0) {
            mControlType = ControlType.DATALIST;
        } else if (isCheckField) {
            mControlType = ControlType.TOGGLE;
        } else {
            mControlType = ControlType.TEXT;
        }
        mMaxLength = maxLength;
        mHeuristicType = heuristicType;
        mServerType = serverType;
        mServerPredictions = serverPredictions;
        mComputedType = computedType;
        mBounds = new RectF(left, top, right, bottom);
        mVisible = visible;
        mAutofilled = isAutofilled;
    }

    public @ControlType int getControlType() {
        return mControlType;
    }

    public RectF getBounds() {
        return mBounds;
    }

    public void updateBounds(RectF bounds) {
        mBounds = bounds;
    }

    public void setBoundsInContainerViewCoordinates(RectF bounds) {
        mBoundsInContainerViewCoordinates = bounds;
    }

    public RectF getBoundsInContainerViewCoordinates() {
        return mBoundsInContainerViewCoordinates;
    }

    /** @return value of field. */
    @CalledByNative
    public String getValue() {
        return mValue;
    }

    public void setAutofillValue(String value) {
        mValue = value;
        updateAutofillState(true);
    }

    public void setChecked(boolean checked) {
        mIsChecked = checked;
        updateAutofillState(true);
    }

    @CalledByNative
    private void updateValue(String value) {
        mValue = value;
        updateAutofillState(false);
    }

    public boolean getVisible() {
        return mVisible;
    }

    @CalledByNative
    private void updateVisible(boolean visible) {
        mVisible = visible;
    }

    @CalledByNative
    private void updateFieldTypes(
            String serverType, String computedType, String[] serverPredictions) {
        mServerType = serverType;
        mComputedType = computedType;
        mServerPredictions = serverPredictions;
    }

    public String getServerType() {
        return mServerType;
    }

    public String getComputedType() {
        return mComputedType;
    }

    public String[] getServerPredictions() {
        return mServerPredictions;
    }

    public String getServerPredictionsString() {
        String[] predictions = getServerPredictions();
        return (predictions != null && predictions.length > 0)
                ? String.join(",", predictions)
                : getEmptyServerPredictionsString();
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    public static String getEmptyServerPredictionsString() {
        return "NO_SERVER_DATA";
    }

    @CalledByNative
    public boolean isChecked() {
        return mIsChecked;
    }

    public boolean hasPreviouslyAutofilled() {
        return mPreviouslyAutofilled;
    }

    @CalledByNative
    public boolean isAutofilled() {
        return mAutofilled;
    }

    private void updateAutofillState(boolean autofilled) {
        if (mAutofilled && !autofilled) mPreviouslyAutofilled = true;
        mAutofilled = autofilled;
    }

    public void setAutofillId(AutofillId id) {
        mAutofillId = id;
    }

    public AutofillId getAutofillId() {
        return mAutofillId;
    }

    @CalledByNative
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    public static FormFieldData createFormFieldData(
            String name,
            String label,
            String value,
            String autocompleteAttr,
            boolean shouldAutocomplete,
            String placeholder,
            String type,
            String id,
            String[] optionValues,
            String[] optionContents,
            boolean isCheckField,
            boolean isChecked,
            int maxLength,
            String heuristicType,
            String serverType,
            String computedType,
            String[] serverPredictions,
            float left,
            float top,
            float right,
            float bottom,
            String[] datalistValues,
            String[] datalistLabels,
            boolean visible,
            boolean isAutofilled) {
        return new FormFieldData(
                name,
                label,
                value,
                autocompleteAttr,
                shouldAutocomplete,
                placeholder,
                type,
                id,
                optionValues,
                optionContents,
                isCheckField,
                isChecked,
                maxLength,
                heuristicType,
                serverType,
                computedType,
                serverPredictions,
                left,
                top,
                right,
                bottom,
                datalistValues,
                datalistLabels,
                visible,
                isAutofilled);
    }
}