Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7bdeec91 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update to ToT RemoteCompose" into main

parents 3b212f6f 52096eb4
Loading
Loading
Loading
Loading
+79 −2
Original line number Diff line number Diff line
@@ -16,20 +16,33 @@
package com.android.internal.widget.remotecompose.accessibility;

import android.graphics.Rect;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;

import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent;

import java.util.List;

public class AndroidPlatformSemanticNodeApplier
        extends BaseSemanticNodeApplier<AccessibilityNodeInfo> {

    private static final String ROLE_DESCRIPTION_KEY = "AccessibilityNodeInfo.roleDescription";

    private final View mPlayer;

    public AndroidPlatformSemanticNodeApplier(View player) {
        this.mPlayer = player;
    }

    @Override
    protected void setClickable(AccessibilityNodeInfo nodeInfo, boolean clickable) {
        nodeInfo.setClickable(clickable);
        if (clickable) {
            nodeInfo.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
            nodeInfo.addAction(AccessibilityAction.ACTION_CLICK);
        } else {
            nodeInfo.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
            nodeInfo.removeAction(AccessibilityAction.ACTION_CLICK);
        }
    }

@@ -83,4 +96,68 @@ public class AndroidPlatformSemanticNodeApplier
    protected void setUniqueId(AccessibilityNodeInfo nodeInfo, String id) {
        nodeInfo.setUniqueId(id);
    }

    @Override
    protected void applyScrollable(
            AccessibilityNodeInfo nodeInfo,
            ScrollableComponent.ScrollAxisRange scrollAxis,
            int scrollDirection) {
        nodeInfo.setScrollable(true);
        nodeInfo.addAction(AccessibilityAction.ACTION_SCROLL_TO_POSITION);
        nodeInfo.addAction(AccessibilityAction.ACTION_SET_PROGRESS);

        nodeInfo.setGranularScrollingSupported(true);

        if (scrollAxis.canScrollForward()) {
            nodeInfo.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
            if (scrollDirection == RootContentBehavior.SCROLL_VERTICAL) {
                nodeInfo.addAction(AccessibilityAction.ACTION_SCROLL_DOWN);
                nodeInfo.addAction(AccessibilityAction.ACTION_PAGE_DOWN);
            } else if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
                // TODO handle RTL
                nodeInfo.addAction(AccessibilityAction.ACTION_SCROLL_RIGHT);
                nodeInfo.addAction(AccessibilityAction.ACTION_PAGE_RIGHT);
            }
        }

        if (scrollAxis.canScrollBackwards()) {
            nodeInfo.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
            if (scrollDirection == RootContentBehavior.SCROLL_VERTICAL) {
                nodeInfo.addAction(AccessibilityAction.ACTION_SCROLL_UP);
                nodeInfo.addAction(AccessibilityAction.ACTION_PAGE_UP);
            } else if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
                // TODO handle RTL
                nodeInfo.addAction(AccessibilityAction.ACTION_SCROLL_LEFT);
                nodeInfo.addAction(AccessibilityAction.ACTION_PAGE_LEFT);
            }
        }

        // TODO correct values
        nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));

        if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
            nodeInfo.setClassName("android.widget.HorizontalScrollView");
        } else {
            nodeInfo.setClassName("android.widget.ScrollView");
        }
    }

    @Override
    protected void applyListItem(AccessibilityNodeInfo nodeInfo, int parentId) {
        nodeInfo.addAction(AccessibilityAction.ACTION_SHOW_ON_SCREEN);
        nodeInfo.setScreenReaderFocusable(true);
        nodeInfo.setFocusable(true);
        nodeInfo.setParent(mPlayer, parentId);

        // TODO correct values
        nodeInfo.setCollectionItemInfo(
                AccessibilityNodeInfo.CollectionItemInfo.obtain(1, 1, 0, 1, false));
    }

    @Override
    public void addChildren(AccessibilityNodeInfo nodeInfo, List<Integer> childIds) {
        for (int id : childIds) {
            nodeInfo.addChild(mPlayer, id);
        }
    }
}
+31 −4
Original line number Diff line number Diff line
@@ -16,11 +16,15 @@
package com.android.internal.widget.remotecompose.accessibility;

import android.graphics.Rect;
import android.util.Log;

import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySemantics;
import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics;
import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent;
import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent.ScrollAxisRange;

import java.util.List;

@@ -37,6 +41,8 @@ import java.util.List;
 * @param <N> The type of node this applier works with.
 */
public abstract class BaseSemanticNodeApplier<N> implements SemanticNodeApplier<N> {
    private static final String LOG_TAG = "RemoteCompose";

    @Override
    public void applyComponent(
            RemoteComposeDocumentAccessibility remoteComposeAccessibility,
@@ -74,6 +80,15 @@ public abstract class BaseSemanticNodeApplier<N> implements SemanticNodeApplier<
        if (getText(nodeInfo) == null && getContentDescription(nodeInfo) == null) {
            setContentDescription(nodeInfo, "");
        }

        if (component.getParent() instanceof LayoutComponent) {
            LayoutComponent parent = (LayoutComponent) component.getParent();
            ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);

            if (scrollable != null) {
                applyListItem(nodeInfo, parent.getComponentId());
            }
        }
    }

    protected void applySemantics(
@@ -106,6 +121,15 @@ public abstract class BaseSemanticNodeApplier<N> implements SemanticNodeApplier<
                    }

                    applyRole(accessibleComponent.getRole(), nodeInfo);
                } else if (semantic instanceof ScrollableComponent) {
                    ScrollableComponent scrollableSemantic = (ScrollableComponent) semantic;

                    if (scrollableSemantic.supportsScrollByOffset()) {
                        ScrollAxisRange scrollAxis = scrollableSemantic.getScrollAxisRange();
                        applyScrollable(nodeInfo, scrollAxis, scrollableSemantic.scrollDirection());
                    }
                } else {
                    Log.w(LOG_TAG, "Unknown semantic: " + semantic);
                }
            }
        }
@@ -154,10 +178,8 @@ public abstract class BaseSemanticNodeApplier<N> implements SemanticNodeApplier<
            N nodeInfo,
            RemoteComposeDocumentAccessibility remoteComposeAccessibility) {
        if (textId != null) {
            setText(
                    nodeInfo,
                    appendNullable(
                            getText(nodeInfo), remoteComposeAccessibility.stringValue(textId)));
            String value = remoteComposeAccessibility.stringValue(textId);
            setText(nodeInfo, appendNullable(getText(nodeInfo), value));
        }
    }

@@ -205,4 +227,9 @@ public abstract class BaseSemanticNodeApplier<N> implements SemanticNodeApplier<
    protected abstract void setBoundsInScreen(N nodeInfo, Rect bounds);

    protected abstract void setUniqueId(N nodeInfo, String s);

    protected abstract void applyScrollable(
            N nodeInfo, ScrollAxisRange scrollAxis, int scrollDirection);

    protected abstract void applyListItem(N nodeInfo, int parentId);
}
+84 −6
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.internal.widget.remotecompose.accessibility;
import android.annotation.Nullable;
import android.graphics.PointF;
import android.os.Bundle;
import android.view.accessibility.AccessibilityNodeInfo;

import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -31,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySemantics;
import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics;
import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent;

import java.util.ArrayList;
import java.util.Collections;
@@ -95,14 +97,90 @@ public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibi
        return result;
    }

    @Override
    public boolean performAction(Component component, int action, Bundle arguments) {
        if (action == ACTION_CLICK) {
            mDocument.performClick(mRemoteContext, component.getComponentId());
            return true;
        boolean needsRepaint = true;

        try {
            if (isClickAction(action)) {
                return performClick(component);
            } else if (isScrollForwardAction(action)) {
                return scrollByOffset(mRemoteContext, component, -500) != 0;
            } else if (isScrollBackwardAction(action)) {
                return scrollByOffset(mRemoteContext, component, 500) != 0;
            } else if (isShowOnScreenAction(action)) {
                return showOnScreen(mRemoteContext, component);
            } else {
                needsRepaint = false;
                return false;
            }
        } finally {
            if (needsRepaint) {
                mDocument.needsRepaint();
            }
        }
    }

    private static boolean isShowOnScreenAction(int action) {
        return action == android.R.id.accessibilityActionShowOnScreen;
    }

    private static boolean isScrollBackwardAction(int action) {
        return action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
                || action == android.R.id.accessibilityActionScrollUp
                || action == android.R.id.accessibilityActionScrollLeft;
    }

    private static boolean isScrollForwardAction(int action) {
        return action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
                || action == android.R.id.accessibilityActionScrollDown
                || action == android.R.id.accessibilityActionScrollRight;
    }

    private static boolean isClickAction(int action) {
        return action == AccessibilityNodeInfo.ACTION_CLICK;
    }

    private boolean showOnScreen(RemoteContext context, Component component) {
        if (component.getParent() instanceof LayoutComponent) {
            LayoutComponent parent = (LayoutComponent) component.getParent();
            ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);

            if (scrollable != null) {
                scrollable.showOnScreen(context, component.getComponentId());
                return true;
            }
        }

        return false;
    }

    /**
     * scroll content by the given offset
     *
     * @param context
     * @param component
     * @param pixels
     * @return
     */
    public int scrollByOffset(RemoteContext context, Component component, int pixels) {
        ScrollableComponent scrollable = component.selfOrModifier(ScrollableComponent.class);

        if (scrollable != null) {
            return scrollable.scrollByOffset(context, pixels);
        }

        return 0;
    }

    /**
     * Perform a click on the given component
     *
     * @param component
     * @return
     */
    public boolean performClick(Component component) {
        mDocument.performClick(mRemoteContext, component.getComponentId());
        return true;
    }

    @Nullable
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ public class PlatformRemoteComposeAccessibilityRegistrar
                player,
                new CoreDocumentAccessibility(
                        coreDocument, ((RemoteContextAware) player).getRemoteContext()),
                new AndroidPlatformSemanticNodeApplier());
                new AndroidPlatformSemanticNodeApplier(player));
    }

    public void setAccessibilityDelegate(View remoteComposePlayer, CoreDocument document) {
+24 −32
Original line number Diff line number Diff line
@@ -33,10 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen
import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySemantics;
import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent.Mode;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;

public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
    private final RemoteComposeDocumentAccessibility mRemoteDocA11y;
@@ -58,7 +55,7 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
                player,
                new CoreDocumentAccessibility(
                        coreDocument, ((RemoteContextAware) player).getRemoteContext()),
                new AndroidPlatformSemanticNodeApplier());
                new AndroidPlatformSemanticNodeApplier(player));
    }

    /**
@@ -96,35 +93,17 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
     */
    @Override
    protected void getVisibleVirtualViews(IntArray virtualViewIds) {
        Stack<Integer> toVisit = new Stack<>();
        Set<Integer> visited = new HashSet<>();
        Component rootComponent = mRemoteDocA11y.findComponentById(RootId);

        toVisit.push(RootId);

        while (!toVisit.isEmpty()) {
            Integer componentId = toVisit.remove(0);

            if (visited.add(componentId)) {
                Component component = mRemoteDocA11y.findComponentById(componentId);

                // Only include the root when it has semantics such as content description
                if (!RootId.equals(componentId)
                        || !mRemoteDocA11y.semanticModifiersForComponent(component).isEmpty()) {
                    virtualViewIds.add(componentId);
        if (rootComponent == null
                || !mRemoteDocA11y.semanticModifiersForComponent(rootComponent).isEmpty()) {
            virtualViewIds.add(RootId);
        }

                if (component != null) {
                    Mode mergeMode = mRemoteDocA11y.mergeMode(component);

                    if (mergeMode == Mode.SET) {
                        List<Integer> childViews =
                                mRemoteDocA11y.semanticallyRelevantChildComponents(
                                        component, false);

                        toVisit.addAll(childViews);
                    }
                }
            }
        List<Integer> children =
                mRemoteDocA11y.semanticallyRelevantChildComponents(rootComponent, false);
        for (int child : children) {
            virtualViewIds.add(child);
        }
    }

@@ -150,6 +129,13 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
        List<AccessibilitySemantics> semantics =
                mRemoteDocA11y.semanticModifiersForComponent(component);
        mApplier.applyComponent(mRemoteDocA11y, node, component, semantics);

        if (mergeMode == Mode.SET) {
            List<Integer> childViews =
                    mRemoteDocA11y.semanticallyRelevantChildComponents(component, false);

            mApplier.addChildren(node, childViews);
        }
    }

    @Override
@@ -161,7 +147,13 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
        Component component = mRemoteDocA11y.findComponentById(virtualViewId);

        if (component != null) {
            return mRemoteDocA11y.performAction(component, action, arguments);
            boolean performed = mRemoteDocA11y.performAction(component, action, arguments);

            if (performed) {
                invalidateRoot();
            }

            return performed;
        } else {
            return false;
        }
Loading