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

Commit 52096eb4 authored by Nicolas Roard's avatar Nicolas Roard
Browse files

Update to ToT RemoteCompose

Bug: 339721781
Flag: EXEMPT External Libraries
Test: in GoB
Change-Id: Ic9e03d59045efcb0476812957e3619068bb02b73
parent ac450eb2
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