chromium/content/public/android/java/src/org/chromium/content/browser/accessibility/AssistDataBuilder.java

// Copyright 2024 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.content.browser.accessibility;

import android.app.assist.AssistStructure.ViewNode;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;
import android.view.ViewStructure;
import android.view.ViewStructure.HtmlInfo;

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

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

/**
 * Basic helper class to build ViewStructure objects for the WebContents in Chrome. The tree is
 * commonly referred to as the "AssistData" tree, hence the name, and to keep it separate from the
 * existing {@link ViewStructureBuilder.java} class which will be replaced by this class once the
 * unification of the code-paths is complete. This class can be used by the Native-side code {@link
 * web_contents_accessibility_android.cc} to construct objects for the virtual view hierarchy to
 * provide to the Android framework.
 */
@JNINamespace("content")
public class AssistDataBuilder {
    private static final String CSS_DISPLAY_BUNDLE_KEY = "display";
    private static final String METADATA_BUNDLE_KEY = "metadata";

    @CalledByNative
    public void populateBaseProperties(ViewStructure node, String className, int childCount) {
        node.setClassName(className);
        node.setChildCount(childCount);
    }

    @CalledByNative
    public void populateTextProperties(
            ViewStructure node,
            String text,
            boolean hasSelection,
            int selStart,
            int selEnd,
            int color,
            int bgcolor,
            float size,
            boolean bold,
            boolean italic,
            boolean underline,
            boolean lineThrough) {
        if (hasSelection) {
            node.setText(text, selStart, selEnd);
        } else {
            node.setText(text);
        }

        // If size is smaller than 0, then style information does not exist.
        if (size >= 0.0) {
            int style =
                    (bold ? ViewNode.TEXT_STYLE_BOLD : 0)
                            | (italic ? ViewNode.TEXT_STYLE_ITALIC : 0)
                            | (underline ? ViewNode.TEXT_STYLE_UNDERLINE : 0)
                            | (lineThrough ? ViewNode.TEXT_STYLE_STRIKE_THRU : 0);
            node.setTextStyle(size, color, bgcolor, style);
        }
    }

    // Stubbed.
    @CalledByNative
    public void populateBoundsProperties(
            ViewStructure node,
            int absoluteLeft,
            int absoluteTop,
            int absoluteWidth,
            int absoluteHeight,
            AccessibilityDelegate.AccessibilityCoordinates accessibilityCoordinates,
            View view) {
        Rect rect =
                new Rect(
                        absoluteLeft,
                        absoluteTop,
                        absoluteLeft + absoluteWidth,
                        absoluteTop + absoluteHeight);
        AccessibilityNodeInfoBuilder.convertWebRectToAndroidCoordinates(
                rect, node.getExtras(), accessibilityCoordinates, view);

        node.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
    }

    @CalledByNative
    public void populateHTMLProperties(
            ViewStructure node, String htmlTag, String cssDisplay, String[][] htmlAttributes) {
        HtmlInfo.Builder htmlBuilder = node.newHtmlInfoBuilder(htmlTag);
        if (htmlBuilder == null) {
            return;
        }

        htmlBuilder.addAttribute(CSS_DISPLAY_BUNDLE_KEY, cssDisplay);
        for (String[] attr : htmlAttributes) {
            htmlBuilder.addAttribute(attr[0], attr[1]);
        }
        node.setHtmlInfo(htmlBuilder.build());
    }

    @CalledByNative
    public void populateHTMLMetadataProperties(ViewStructure node, String[] metadataStrings) {
        Bundle extras = node.getExtras();
        extras.putStringArrayList(
                METADATA_BUNDLE_KEY, new ArrayList<String>(Arrays.asList(metadataStrings)));
    }

    @CalledByNative
    public ViewStructure addChildNode(ViewStructure node, int childIndex) {
        return node.asyncNewChild(childIndex);
    }

    @CalledByNative
    public void commitNode(ViewStructure node) {
        node.asyncCommit();
    }
}