diff options
127 files changed, 3083 insertions, 186 deletions
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java new file mode 100644 index 000000000000..410e021f5162 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.accessibility; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Rect; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.internal.widget.remotecompose.core.operations.layout.Component; +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 java.util.List; + +public class AndroidPlatformSemanticNodeApplier + implements SemanticNodeApplier<AccessibilityNodeInfo, Component, AccessibilitySemantics> { + + private static final String ROLE_DESCRIPTION_KEY = "AccessibilityNodeInfo.roleDescription"; + + @Override + public void applyComponent( + @NonNull + RemoteComposeDocumentAccessibility<Component, AccessibilitySemantics> + remoteComposeAccessibility, + AccessibilityNodeInfo nodeInfo, + Component component, + List<AccessibilitySemantics> semantics) { + if (component instanceof AccessibleComponent) { + applyContentDescription( + ((AccessibleComponent) component).getContentDescriptionId(), + nodeInfo, + remoteComposeAccessibility); + + applyRole(((AccessibleComponent) component).getRole(), nodeInfo); + } + + applySemantics(remoteComposeAccessibility, nodeInfo, semantics); + + float[] locationInWindow = new float[2]; + component.getLocationInWindow(locationInWindow); + Rect bounds = + new Rect( + (int) locationInWindow[0], + (int) locationInWindow[1], + (int) (locationInWindow[0] + component.getWidth()), + (int) (locationInWindow[1] + component.getHeight())); + //noinspection deprecation + nodeInfo.setBoundsInParent(bounds); + nodeInfo.setBoundsInScreen(bounds); + + if (component instanceof AccessibleComponent) { + applyContentDescription( + ((AccessibleComponent) component).getContentDescriptionId(), + nodeInfo, + remoteComposeAccessibility); + + applyText( + ((AccessibleComponent) component).getTextId(), + nodeInfo, + remoteComposeAccessibility); + + applyRole(((AccessibleComponent) component).getRole(), nodeInfo); + } + + applySemantics(remoteComposeAccessibility, nodeInfo, semantics); + + if (nodeInfo.getText() == null && nodeInfo.getContentDescription() == null) { + nodeInfo.setContentDescription(""); + } + } + + public void applySemantics( + RemoteComposeDocumentAccessibility<Component, AccessibilitySemantics> + remoteComposeAccessibility, + AccessibilityNodeInfo nodeInfo, + List<AccessibilitySemantics> semantics) { + for (AccessibilitySemantics semantic : semantics) { + if (semantic.isInterestingForSemantics()) { + if (semantic instanceof CoreSemantics) { + applyCoreSemantics( + remoteComposeAccessibility, nodeInfo, (CoreSemantics) semantic); + } else if (semantic instanceof AccessibleComponent) { + AccessibleComponent s = (AccessibleComponent) semantic; + + applyContentDescription( + s.getContentDescriptionId(), nodeInfo, remoteComposeAccessibility); + + applyRole(s.getRole(), nodeInfo); + + applyText(s.getTextId(), nodeInfo, remoteComposeAccessibility); + + if (s.isClickable()) { + nodeInfo.setClickable(true); + nodeInfo.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK); + } + } + } + } + } + + private void applyCoreSemantics( + RemoteComposeDocumentAccessibility<Component, AccessibilitySemantics> + remoteComposeAccessibility, + AccessibilityNodeInfo nodeInfo, + CoreSemantics semantics) { + applyContentDescription( + semantics.getContentDescriptionId(), nodeInfo, remoteComposeAccessibility); + + applyRole(semantics.getRole(), nodeInfo); + + applyText(semantics.getTextId(), nodeInfo, remoteComposeAccessibility); + + applyStateDescription( + semantics.getStateDescriptionId(), nodeInfo, remoteComposeAccessibility); + + nodeInfo.setEnabled(semantics.mEnabled); + } + + void applyRole(@Nullable AccessibleComponent.Role role, AccessibilityNodeInfo nodeInfo) { + if (role != null) { + nodeInfo.getExtras().putCharSequence(ROLE_DESCRIPTION_KEY, role.getDescription()); + } + } + + void applyContentDescription( + @Nullable Integer contentDescriptionId, + AccessibilityNodeInfo nodeInfo, + RemoteComposeDocumentAccessibility<Component, AccessibilitySemantics> + remoteComposeAccessibility) { + if (contentDescriptionId != null) { + nodeInfo.setContentDescription( + remoteComposeAccessibility.stringValue(contentDescriptionId)); + } + } + + void applyText( + @Nullable Integer textId, + AccessibilityNodeInfo nodeInfo, + RemoteComposeDocumentAccessibility<Component, AccessibilitySemantics> + remoteComposeAccessibility) { + if (textId != null) { + nodeInfo.setText(remoteComposeAccessibility.stringValue(textId)); + } + } + + void applyStateDescription( + @Nullable Integer stateDescriptionId, + AccessibilityNodeInfo nodeInfo, + RemoteComposeDocumentAccessibility<Component, AccessibilitySemantics> + remoteComposeAccessibility) { + if (stateDescriptionId != null) { + nodeInfo.setStateDescription( + remoteComposeAccessibility.stringValue(stateDescriptionId)); + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java new file mode 100644 index 000000000000..66a7f0257e0e --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.accessibility; + +import android.annotation.Nullable; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Bundle; + +import com.android.internal.widget.remotecompose.core.CoreDocument; +import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation; +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.operations.layout.RootLayoutComponent; +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers; +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation; +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 java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Java Player implementation of the {@link RemoteComposeDocumentAccessibility} interface. Each item + * in the semantic tree is a {@link Component} from the remote Compose UI. Each Component can have a + * list of modifiers that must be tagged with {@link AccessibilitySemantics} either incidentally + * (see {@link ClickModifierOperation}) or explicitly (see {@link CoreSemantics}). + */ +public class CoreDocumentAccessibility + implements RemoteComposeDocumentAccessibility<Component, AccessibilitySemantics> { + private final CoreDocument mDocument; + + private final Rect mMissingBounds = new Rect(0, 0, 1, 1); + + public CoreDocumentAccessibility(CoreDocument document) { + this.mDocument = document; + } + + @Nullable + @Override + public Integer getComponentIdAt(PointF point) { + return RootId; + } + + @Override + public @Nullable Component findComponentById(int virtualViewId) { + RootLayoutComponent root = mDocument.getRootLayoutComponent(); + + if (root == null || virtualViewId == -1) { + return root; + } + + return componentStream(root) + .filter(op -> op.getComponentId() == virtualViewId) + .findFirst() + .orElse(null); + } + + @Override + public List<CoreSemantics.Mode> mergeMode(Component component) { + if (!(component instanceof LayoutComponent)) { + return Collections.singletonList(CoreSemantics.Mode.SET); + } + + return ((LayoutComponent) component) + .getComponentModifiers().getList().stream() + .filter(i -> i instanceof AccessibleComponent) + .map(i -> ((AccessibleComponent) i).getMode()) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + @Override + public boolean performAction(Component component, int action, Bundle arguments) { + if (action == ACTION_CLICK) { + mDocument.performClick(component.getComponentId()); + return true; + } else { + return false; + } + } + + @Nullable + @Override + public String stringValue(int id) { + Object value = mDocument.getRemoteComposeState().getFromId(id); + return value != null ? String.valueOf(value) : null; + } + + @Override + public List<AccessibilitySemantics> semanticModifiersForComponent(Component component) { + if (!(component instanceof LayoutComponent)) { + return Collections.emptyList(); + } + + List<ModifierOperation> modifiers = + ((LayoutComponent) component).getComponentModifiers().getList(); + + return modifiers.stream() + .filter( + it -> + it instanceof AccessibilitySemantics + && ((AccessibilitySemantics) it) + .isInterestingForSemantics()) + .map(i -> (AccessibilitySemantics) i) + .collect(Collectors.toList()); + } + + @Override + public List<Integer> semanticallyRelevantChildComponents(Component component) { + return componentStream(component) + .filter(i -> i.getComponentId() != component.getComponentId()) + .filter(CoreDocumentAccessibility::isInteresting) + .map(Component::getComponentId) + .collect(Collectors.toList()); + } + + static Stream<Component> componentStream(Component root) { + return Stream.concat( + Stream.of(root), + root.mList.stream() + .flatMap( + op -> { + if (op instanceof Component) { + return componentStream((Component) op); + } else { + return Stream.empty(); + } + })); + } + + static Stream<ModifierOperation> modifiersStream(Component component) { + return component.mList.stream() + .filter(it -> it instanceof ComponentModifiers) + .flatMap(it -> ((ComponentModifiers) it).getList().stream()); + } + + static boolean isInteresting(Component component) { + boolean interesting = + isContainerWithSemantics(component) + || modifiersStream(component) + .anyMatch(CoreDocumentAccessibility::isModifierWithSemantics); + + return interesting && component.isVisible(); + } + + static boolean isModifierWithSemantics(ModifierOperation modifier) { + return modifier instanceof AccessibilitySemantics + && ((AccessibilitySemantics) modifier).isInterestingForSemantics(); + } + + static boolean isContainerWithSemantics(Component component) { + if (component instanceof AccessibilitySemantics) { + return ((AccessibilitySemantics) component).isInterestingForSemantics(); + } + + if (!(component instanceof LayoutComponent)) { + return false; + } + + return ((LayoutComponent) component) + .getComponentModifiers().getList().stream() + .anyMatch(CoreDocumentAccessibility::isModifierWithSemantics); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java new file mode 100644 index 000000000000..c9ad28ac0731 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.accessibility; + +import static com.android.internal.widget.remotecompose.accessibility.RemoteComposeDocumentAccessibility.RootId; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.PointF; +import android.os.Bundle; +import android.util.IntArray; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.internal.widget.ExploreByTouchHelper; +import com.android.internal.widget.remotecompose.core.CoreDocument; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; +import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySemantics; +import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics.Mode; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +public class PlatformRemoteComposeTouchHelper<N, C, S> extends ExploreByTouchHelper { + private final RemoteComposeDocumentAccessibility<C, S> mRemoteDocA11y; + + private final SemanticNodeApplier<AccessibilityNodeInfo, C, S> mApplier; + + public PlatformRemoteComposeTouchHelper( + View host, + RemoteComposeDocumentAccessibility<C, S> remoteDocA11y, + SemanticNodeApplier<AccessibilityNodeInfo, C, S> applier) { + super(host); + this.mRemoteDocA11y = remoteDocA11y; + this.mApplier = applier; + } + + public static PlatformRemoteComposeTouchHelper< + AccessibilityNodeInfo, Component, AccessibilitySemantics> + forRemoteComposePlayer(View player, @NonNull CoreDocument coreDocument) { + return new PlatformRemoteComposeTouchHelper<>( + player, + new CoreDocumentAccessibility(coreDocument), + new AndroidPlatformSemanticNodeApplier()); + } + + /** + * Gets the virtual view ID at a given location on the screen. + * + * <p>This method is called by the Accessibility framework to determine which virtual view, if + * any, is located at a specific point on the screen. It uses the {@link + * RemoteComposeDocumentAccessibility#getComponentIdAt(PointF)} method to find the ID of the + * component at the given coordinates. + * + * @param x The x-coordinate of the location in pixels. + * @param y The y-coordinate of the location in pixels. + * @return The ID of the virtual view at the given location, or {@link #INVALID_ID} if no + * virtual view is found at that location. + */ + @Override + protected int getVirtualViewAt(float x, float y) { + Integer root = mRemoteDocA11y.getComponentIdAt(new PointF(x, y)); + + if (root == null) { + return INVALID_ID; + } + + return root; + } + + /** + * Populates a list with the visible virtual view IDs. + * + * <p>This method is called by the accessibility framework to retrieve the IDs of all visible + * virtual views in the accessibility hierarchy. It traverses the hierarchy starting from the + * root node (RootId) and adds the ID of each visible view to the provided list. + * + * @param virtualViewIds The list to be populated with the visible virtual view IDs. + */ + @Override + protected void getVisibleVirtualViews(IntArray virtualViewIds) { + Stack<Integer> toVisit = new Stack<>(); + Set<Integer> visited = new HashSet<>(); + + toVisit.push(RootId); + + while (!toVisit.isEmpty()) { + Integer componentId = toVisit.remove(0); + + if (visited.add(componentId)) { + virtualViewIds.add(componentId); + + C component = mRemoteDocA11y.findComponentById(componentId); + + if (component != null) { + boolean allSet = + mRemoteDocA11y.mergeMode(component).stream() + .allMatch(i -> i == Mode.SET); + + if (allSet) { + List<Integer> childViews = + mRemoteDocA11y.semanticallyRelevantChildComponents(component); + + toVisit.addAll(childViews); + } + } + } + } + } + + @Override + public void onPopulateNodeForVirtualView( + int virtualViewId, @NonNull AccessibilityNodeInfo node) { + C component = mRemoteDocA11y.findComponentById(virtualViewId); + + List<Mode> mode = mRemoteDocA11y.mergeMode(component); + + if (mode.contains(Mode.MERGE)) { + List<Integer> childViews = + mRemoteDocA11y.semanticallyRelevantChildComponents(component); + + for (Integer childView : childViews) { + onPopulateNodeForVirtualView(childView, node); + } + } + + List<S> semantics = mRemoteDocA11y.semanticModifiersForComponent(component); + mApplier.applyComponent(mRemoteDocA11y, node, component, semantics); + } + + @Override + protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { + // TODO + } + + @Override + protected boolean onPerformActionForVirtualView( + int virtualViewId, int action, @Nullable Bundle arguments) { + C component = mRemoteDocA11y.findComponentById(virtualViewId); + + if (component != null) { + return mRemoteDocA11y.performAction(component, action, arguments); + } else { + return false; + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/RemoteComposeDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/RemoteComposeDocumentAccessibility.java new file mode 100644 index 000000000000..14977becd2d7 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/accessibility/RemoteComposeDocumentAccessibility.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.accessibility; + +import android.annotation.Nullable; +import android.graphics.PointF; +import android.os.Bundle; +import android.view.View; + +import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics; + +import java.util.List; + +/** + * Interface for interacting with the accessibility features of a remote Compose UI. This interface + * provides methods to perform actions, retrieve state, and query the accessibility tree of the + * remote Compose UI. + * + * @param <C> The type of component in the remote Compose UI. + * @param <S> The type representing semantic modifiers applied to components. + */ +public interface RemoteComposeDocumentAccessibility<C, S> { + // Matches ExploreByTouchHelper.HOST_ID + Integer RootId = View.NO_ID; + + // androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CLICK + int ACTION_CLICK = 0x00000010; + + /** + * Performs the specified action on the given component. + * + * @param component The component on which to perform the action. + * @param action The action to perform. + * @param arguments Optional arguments for the action. + * @return {@code true} if the action was performed successfully, {@code false} otherwise. + */ + boolean performAction(C component, int action, Bundle arguments); + + /** + * Retrieves the string value associated with the given ID. + * + * @param id The ID to retrieve the string value for. + * @return The string value associated with the ID, or {@code null} if no such value exists. + */ + @Nullable + String stringValue(int id); + + /** + * Retrieves a list of child view IDs semantically contained within the given component/virtual + * view. These may later be hidden from accessibility services by properties, but should contain + * only possibly semantically relevant virtual views. + * + * @param component The component to retrieve child view IDs from, or [RootId] for the top + * level. + * @return A list of integer IDs representing the child views of the component. + */ + List<Integer> semanticallyRelevantChildComponents(C component); + + /** + * Retrieves the semantic modifiers associated with a given component. + * + * @param component The component for which to retrieve semantic modifiers. + * @return A list of semantic modifiers applicable to the component. + */ + List<S> semanticModifiersForComponent(C component); + + /** + * Gets all applied merge modes of the given component. A Merge mode is one of Set, Merge or + * Clear and describes how to apply and combine hierarchical semantics. + * + * @param component The component to merge the mode for. + * @return A list of merged modes, potentially conflicting but to be resolved by the caller. + */ + List<CoreSemantics.Mode> mergeMode(C component); + + /** + * Finds a component by its ID. + * + * @param id the ID of the component to find + * @return the component with the given ID, or {@code null} if no such component exists + */ + @Nullable + C findComponentById(int id); + + @Nullable + Integer getComponentIdAt(PointF point); +} diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/RemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/RemoteComposeTouchHelper.java new file mode 100644 index 000000000000..4ff7892d8224 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/accessibility/RemoteComposeTouchHelper.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.accessibility; + +import android.annotation.NonNull; +import android.view.View; + +import com.android.internal.widget.remotecompose.core.CoreDocument; + +public class RemoteComposeTouchHelper { + public static View.AccessibilityDelegate forRemoteComposePlayer( + View player, @NonNull CoreDocument coreDocument) { + return new PlatformRemoteComposeTouchHelper<>( + player, + new CoreDocumentAccessibility(coreDocument), + new AndroidPlatformSemanticNodeApplier()); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/SemanticNodeApplier.java b/core/java/com/android/internal/widget/remotecompose/accessibility/SemanticNodeApplier.java new file mode 100644 index 000000000000..4368329478f3 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/accessibility/SemanticNodeApplier.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.accessibility; + +import java.util.List; + +/** + * An interface for applying semantic information to a semantics node. + * + * <p>Implementations of this interface are responsible for taking a node represented by [nodeInfo] + * and applying a list of [semantics] (representing accessible actions and properties) to it. This + * process might involve: - Modifying the node's properties (e.g., content description, clickable + * state). - Adding a child node to represent a specific semantic element. - Performing any other + * action necessary to make the node semantically meaningful and accessible to assistive + * technologies. + * + * @param <N> The type representing information about the node. This could be an Androidx + * `AccessibilityNodeInfoCompat`, or potentially a platform `AccessibilityNodeInfo`. + * @param <C> The type of component in the remote Compose UI. + * @param <S> The type representing a single semantic property or action. + */ +public interface SemanticNodeApplier<N, C, S> { + void applyComponent( + RemoteComposeDocumentAccessibility<C, S> remoteComposeAccessibility, + N nodeInfo, + C component, + List<S> semantics); + + String VIRTUAL_VIEW_ID_KEY = "VirtualViewId"; +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java index 370289a84502..5bc3bca17dda 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java @@ -18,6 +18,7 @@ package com.android.internal.widget.remotecompose.core; import android.annotation.NonNull; import android.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.remotecompose.core.operations.ComponentValue; import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.IntegerExpression; @@ -28,7 +29,6 @@ import com.android.internal.widget.remotecompose.core.operations.layout.ClickMod import com.android.internal.widget.remotecompose.core.operations.layout.Component; import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd; import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation; -import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent; import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd; import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation; import com.android.internal.widget.remotecompose.core.operations.layout.OperationsListEnd; @@ -38,6 +38,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.TouchDow import com.android.internal.widget.remotecompose.core.operations.layout.TouchUpModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation; +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; import java.util.ArrayList; @@ -53,13 +54,14 @@ import java.util.Set; public class CoreDocument { private static final boolean DEBUG = false; + private static final int DOCUMENT_API_LEVEL = 2; @NonNull ArrayList<Operation> mOperations = new ArrayList<>(); @Nullable RootLayoutComponent mRootLayoutComponent = null; @NonNull RemoteComposeState mRemoteComposeState = new RemoteComposeState(); - @NonNull TimeVariables mTimeVariables = new TimeVariables(); + @VisibleForTesting @NonNull public TimeVariables mTimeVariables = new TimeVariables(); // Semantic version of the document @NonNull Version mVersion = new Version(0, 1, 0); @@ -86,6 +88,11 @@ public class CoreDocument { private int mLastId = 1; // last component id when inflating the file + /** Returns a version number that is monotonically increasing. */ + public static int getDocumentApiLevel() { + return DOCUMENT_API_LEVEL; + } + @Nullable public String getContentDescription() { return mContentDescription; @@ -565,6 +572,7 @@ public class CoreDocument { TouchUpModifierOperation currentTouchUpModifier = null; TouchCancelModifierOperation currentTouchCancelModifier = null; LoopOperation currentLoop = null; + ScrollModifierOperation currentScrollModifier = null; mLastId = -1; for (Operation o : operations) { @@ -579,8 +587,8 @@ public class CoreDocument { mLastId = component.getComponentId(); } } else if (o instanceof ComponentEnd) { - if (currentComponent instanceof LayoutComponent) { - ((LayoutComponent) currentComponent).inflate(); + if (currentComponent != null) { + currentComponent.inflate(); } components.remove(components.size() - 1); if (!components.isEmpty()) { @@ -602,6 +610,9 @@ public class CoreDocument { } else if (o instanceof TouchCancelModifierOperation) { currentTouchCancelModifier = (TouchCancelModifierOperation) o; ops = currentTouchCancelModifier.getList(); + } else if (o instanceof ScrollModifierOperation) { + currentScrollModifier = (ScrollModifierOperation) o; + ops = currentScrollModifier.getList(); } else if (o instanceof OperationsListEnd) { ops = currentComponent.getList(); if (currentClickModifier != null) { @@ -616,6 +627,9 @@ public class CoreDocument { } else if (currentTouchCancelModifier != null) { ops.add(currentTouchCancelModifier); currentTouchCancelModifier = null; + } else if (currentScrollModifier != null) { + ops.add(currentScrollModifier); + currentScrollModifier = null; } } else if (o instanceof LoopOperation) { currentLoop = (LoopOperation) o; @@ -881,7 +895,7 @@ public class CoreDocument { } if (mRootLayoutComponent != null) { for (Component component : mAppliedTouchOperations) { - component.onTouchUp(context, this, x, y, true); + component.onTouchUp(context, this, x, y, dx, dy, true); } mAppliedTouchOperations.clear(); } @@ -1039,7 +1053,13 @@ public class CoreDocument { || context.getTheme() == Theme.UNSPECIFIED; } if (apply) { - op.apply(context); + if (op.isDirty() || op instanceof PaintOperation) { + if (op.isDirty() && op instanceof VariableSupport) { + op.markNotDirty(); + ((VariableSupport) op).updateVariables(context); + } + op.apply(context); + } } } if (context.getPaintContext().doesNeedsRepaint() diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/Operation.java index 6f6a0a892964..150ebd0d3dfc 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/Operation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/Operation.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; /** Base interface for RemoteCompose operations */ public abstract class Operation { + private static final boolean ENABLE_DIRTY_FLAG_OPTIMIZATION = true; + /** add the operation to the buffer */ public abstract void write(@NonNull WireBuffer buffer); @@ -33,4 +35,30 @@ public abstract class Operation { /** Debug utility to display an operation + indentation */ @NonNull public abstract String deepToString(@NonNull String indent); + + private boolean mDirty = true; + + /** Mark the operation as "dirty" to indicate it will need to be re-executed. */ + public void markDirty() { + mDirty = true; + } + + /** Mark the operation as "not dirty" */ + public void markNotDirty() { + if (ENABLE_DIRTY_FLAG_OPTIMIZATION) { + mDirty = false; + } + } + + /** + * Returns true if the operation is marked as "dirty" + * + * @return true if dirty + */ + public boolean isDirty() { + if (ENABLE_DIRTY_FLAG_OPTIMIZATION) { + return mDirty; + } + return true; + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java b/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java index 741303a40bc9..06beffcac0de 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java +++ b/core/java/com/android/internal/widget/remotecompose/core/OperationInterface.java @@ -33,4 +33,14 @@ public interface OperationInterface { /** Debug utility to display an operation + indentation */ @NonNull String deepToString(@NonNull String indent); + + /** + * Returns true if the operation is marked as "dirty" + * + * @return true if dirty + */ + boolean isDirty(); + + /** Mark the operation as "dirty" to indicate it will need to be re-executed. */ + void markNotDirty(); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java index 006fe3afb4d8..04e490fa5214 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java +++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java @@ -98,8 +98,10 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostActionOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostNamedActionOperation; +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.MarqueeModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.OffsetModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation; +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RippleModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueFloatChangeActionOperation; @@ -110,6 +112,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation; import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap; +import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics; import com.android.internal.widget.remotecompose.core.types.BooleanConstant; import com.android.internal.widget.remotecompose.core.types.IntegerConstant; import com.android.internal.widget.remotecompose.core.types.LongConstant; @@ -126,6 +129,9 @@ public class Operations { public static final int CLICK_AREA = 64; public static final int ROOT_CONTENT_BEHAVIOR = 65; public static final int ROOT_CONTENT_DESCRIPTION = 103; + // TODO reorder before submitting + public static final int ACCESSIBILITY_SEMANTICS = 250; + // public static final int ACCESSIBILITY_CUSTOM_ACTION = 251; //////////////////////////////////////// // Draw commands @@ -223,6 +229,8 @@ public class Operations { public static final int MODIFIER_ZINDEX = 223; public static final int MODIFIER_GRAPHICS_LAYER = 224; public static final int MODIFIER_SCROLL = 226; + public static final int MODIFIER_MARQUEE = 228; + public static final int MODIFIER_RIPPLE = 229; public static final int LOOP_START = 215; public static final int LOOP_END = 216; @@ -327,6 +335,8 @@ public class Operations { map.put(MODIFIER_ZINDEX, ZIndexModifierOperation::read); map.put(MODIFIER_GRAPHICS_LAYER, GraphicsLayerModifierOperation::read); map.put(MODIFIER_SCROLL, ScrollModifierOperation::read); + map.put(MODIFIER_MARQUEE, MarqueeModifierOperation::read); + map.put(MODIFIER_RIPPLE, RippleModifierOperation::read); map.put(OPERATIONS_LIST_END, OperationsListEnd::read); @@ -362,5 +372,8 @@ public class Operations { map.put(PATH_TWEEN, PathTween::read); map.put(PATH_CREATE, PathCreate::read); map.put(PATH_ADD, PathAppend::read); + + map.put(ACCESSIBILITY_SEMANTICS, CoreSemantics::read); + // map.put(ACCESSIBILITY_CUSTOM_ACTION, CoreSemantics::read); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java index 3a5d68db8a37..0ae7a94b948d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java @@ -15,7 +15,6 @@ */ package com.android.internal.widget.remotecompose.core; -import static com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression.ADD; import static com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression.MUL; import android.annotation.NonNull; @@ -81,6 +80,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent; import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd; import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation; +import com.android.internal.widget.remotecompose.core.operations.layout.OperationsListEnd; import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent; import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout; import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout; @@ -92,8 +92,10 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation; +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.MarqueeModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.OffsetModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation; +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RippleModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation; @@ -1610,7 +1612,7 @@ public class RemoteComposeBuffer { * create and animation based on description and return as an array of floats. see * addAnimatedFloat * - * @param duration the duration of the aimation + * @param duration the duration of the animation in seconds * @param type the type of animation * @param spec the parameters of the animation if any * @param initialValue the initial value if it animates to a start @@ -1699,6 +1701,9 @@ public class RemoteComposeBuffer { float notchMax = this.reserveFloatVariable(); float touchExpressionDirection = direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y; + + ScrollModifierOperation.apply(mBuffer, direction, positionId, max, notchMax); + this.addTouchExpression( positionId, 0f, @@ -1707,20 +1712,13 @@ public class RemoteComposeBuffer { 0f, 3, new float[] { - touchExpressionDirection, - -1, - // TODO: remove this CONTINUOUS_SEC hack... - MUL, - RemoteContext.FLOAT_CONTINUOUS_SEC, - 0f, - MUL, - ADD + touchExpressionDirection, -1, MUL, }, TouchExpression.STOP_NOTCHES_EVEN, new float[] {notches, notchMax}, null); - ScrollModifierOperation.apply(mBuffer, direction, positionId, max, notchMax); + OperationsListEnd.apply(mBuffer); } /** @@ -1786,6 +1784,38 @@ public class RemoteComposeBuffer { ZIndexModifierOperation.apply(mBuffer, value); } + /** Add a ripple effect on touch down as a modifier */ + public void addModifierRipple() { + RippleModifierOperation.apply(mBuffer); + } + + /** + * Add a marquee modifier + * + * @param iterations + * @param animationMode + * @param repeatDelayMillis + * @param initialDelayMillis + * @param spacing + * @param velocity + */ + public void addModifierMarquee( + int iterations, + int animationMode, + float repeatDelayMillis, + float initialDelayMillis, + float spacing, + float velocity) { + MarqueeModifierOperation.apply( + mBuffer, + iterations, + animationMode, + repeatDelayMillis, + initialDelayMillis, + spacing, + velocity); + } + /** * Add a graphics layer * diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java index f5f9e214723b..11e58ba0796f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java @@ -48,6 +48,10 @@ public class RemoteComposeState implements CollectionsAccess { private final IntMap<DataMap> mDataMapMap = new IntMap<>(); private final IntMap<Object> mObjectMap = new IntMap<>(); + // path information + private final IntMap<Object> mPathMap = new IntMap<>(); + private final IntMap<float[]> mPathData = new IntMap<>(); + private final boolean[] mColorOverride = new boolean[MAX_COLORS]; @NonNull private final IntMap<ArrayAccess> mCollectionMap = new IntMap<>(); @@ -131,12 +135,44 @@ public class RemoteComposeState implements CollectionsAccess { } } - private final IntMap<float[]> mPathData = new IntMap<>(); + /** + * Get the path asociated with the Data + * + * @param id + * @return + */ + public Object getPath(int id) { + return mPathMap.get(id); + } + /** + * Cache a path object. Object will be cleared if you update path data. + * + * @param id number asociated with path + * @param path the path object typically Android Path + */ + public void putPath(int id, Object path) { + mPathMap.put(id, path); + } + + /** + * The path data the Array of floats that is asoicated with the path It also removes the current + * path object. + * + * @param id the integer asociated with the data and path + * @param data the array of floats that represents the path + */ public void putPathData(int id, float[] data) { mPathData.put(id, data); + mPathMap.remove(id); } + /** + * Get the path data asociated with the id + * + * @param id number that represents the path + * @return path data + */ public float[] getPathData(int id) { return mPathData.get(id); } @@ -283,7 +319,7 @@ public class RemoteComposeState implements CollectionsAccess { ArrayList<VariableSupport> v = mVarListeners.get(id); if (v != null && mRemoteContext != null) { for (VariableSupport c : v) { - c.updateVariables(mRemoteContext); + c.markDirty(); } } } @@ -426,9 +462,6 @@ public class RemoteComposeState implements CollectionsAccess { * @return */ public int getOpsToUpdate(@NonNull RemoteContext context) { - for (VariableSupport vs : mAllVarListeners) { - vs.updateVariables(context); - } if (mVarListeners.get(RemoteContext.ID_CONTINUOUS_SEC) != null) { return 1; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java index 6eb8463ab308..003acb780715 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java @@ -214,6 +214,13 @@ public abstract class RemoteContext { */ public abstract void hapticEffect(int type); + /** Set the repaint flag. This will trigger a repaint of the current document. */ + public void needsRepaint() { + if (mPaintContext != null) { + mPaintContext.needsRepaint(); + } + } + /** * The context can be used in a few different mode, allowing operations to skip being executed: * - UNSET : all operations will get executed - DATA : only operations dealing with DATA (eg diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java index 14aed2f0c173..0ed6005da389 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java +++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java @@ -29,9 +29,7 @@ public class TimeVariables { * * @param context */ - public void updateTime(@NonNull RemoteContext context) { - LocalDateTime dateTime = - LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly? + public void updateTime(@NonNull RemoteContext context, ZoneId zoneId, LocalDateTime dateTime) { // This define the time in the format // seconds run from Midnight=0 quantized to seconds hour 0..3599 // minutes run from Midnight=0 quantized to minutes 0..1439 @@ -48,8 +46,7 @@ public class TimeVariables { float sec = currentSeconds + dateTime.getNano() * 1E-9f; int day_week = dateTime.getDayOfWeek().getValue(); - ZoneId zone = ZoneId.systemDefault(); - OffsetDateTime offsetDateTime = dateTime.atZone(zone).toOffsetDateTime(); + OffsetDateTime offsetDateTime = dateTime.atZone(zoneId).toOffsetDateTime(); ZoneOffset offset = offsetDateTime.getOffset(); context.loadFloat(RemoteContext.ID_OFFSET_TO_UTC, offset.getTotalSeconds()); @@ -61,4 +58,16 @@ public class TimeVariables { context.loadFloat(RemoteContext.ID_DAY_OF_MONTH, month); context.loadFloat(RemoteContext.ID_WEEK_DAY, day_week); } + + /** + * This class populates all time variables in the system + * + * @param context + */ + public void updateTime(@NonNull RemoteContext context) { + ZoneId zone = ZoneId.systemDefault(); + LocalDateTime dateTime = LocalDateTime.now(zone); + + updateTime(context, zone, dateTime); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java b/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java index 3dda678c2a3a..611ba97c10cb 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java +++ b/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java @@ -15,10 +15,34 @@ */ package com.android.internal.widget.remotecompose.core; +/** Interface used by objects to register for touch events */ public interface TouchListener { + /** + * Called when touch down happens + * + * @param context The players context + * @param x the x location of the down touch + * @param y the y location of the down touch + */ void touchDown(RemoteContext context, float x, float y); + /** + * called on touch up + * + * @param context the players context + * @param x the x location + * @param y the y location + * @param dx the x velocity when the touch up happened + * @param dy the y valocity when the touch up happened + */ void touchUp(RemoteContext context, float x, float y, float dx, float dy); + /** + * Drag event (occur between down and up) + * + * @param context the players context + * @param x the x coord of the drag + * @param y the y coord of the drag + */ void touchDrag(RemoteContext context, float x, float y); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java index e9fa8976637e..1f3e290a0f04 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java +++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java @@ -36,4 +36,7 @@ public interface VariableSupport { * @param context */ void updateVariables(@NonNull RemoteContext context); + + /** Mark the operation as dirty to indicate that the variables it references are out of date. */ + void markDirty(); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java index 27ba6528a703..784897b04991 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java @@ -45,15 +45,39 @@ public class BitmapData extends Operation implements SerializableToString { short mType; short mEncoding; @NonNull final byte[] mBitmap; + + /** The max size of width or height */ public static final int MAX_IMAGE_DIMENSION = 8000; + + /** The data is encoded in the file (default) */ public static final short ENCODING_INLINE = 0; + + /** The data is encoded in the url */ public static final short ENCODING_URL = 1; + + /** The data is encoded as a reference to file */ public static final short ENCODING_FILE = 2; + + /** The data is encoded as PNG_8888 (default) */ public static final short TYPE_PNG_8888 = 0; + + /** The data is encoded as PNG */ public static final short TYPE_PNG = 1; + + /** The data is encoded as RAW 8 bit */ public static final short TYPE_RAW8 = 2; + + /** The data is encoded as RAW 8888 bit */ public static final short TYPE_RAW8888 = 3; + /** + * create a bitmap structure + * + * @param imageId the id to store the image + * @param width the width of the image + * @param height the height of the image + * @param bitmap the data + */ public BitmapData(int imageId, int width, int height, @NonNull byte[] bitmap) { this.mImageId = imageId; this.mImageWidth = width; @@ -61,10 +85,20 @@ public class BitmapData extends Operation implements SerializableToString { this.mBitmap = bitmap; } + /** + * The width of the image + * + * @return the width + */ public int getWidth() { return mImageWidth; } + /** + * The height of the image + * + * @return the height + */ public int getHeight() { return mImageHeight; } @@ -80,6 +114,11 @@ public class BitmapData extends Operation implements SerializableToString { return "BITMAP DATA " + mImageId; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -94,6 +133,15 @@ public class BitmapData extends Operation implements SerializableToString { return OP_CODE; } + /** + * Add the image to the document + * + * @param buffer document to write to + * @param imageId the id the image will be stored under + * @param width the width of the image + * @param height the height of the image + * @param bitmap the data used to store/encode the image + */ public static void apply( @NonNull WireBuffer buffer, int imageId, @@ -107,6 +155,17 @@ public class BitmapData extends Operation implements SerializableToString { buffer.writeBuffer(bitmap); } + /** + * Add the image to the document (using the ehanced encoding) + * + * @param buffer document to write to + * @param imageId the id the image will be stored under + * @param type the type of image + * @param width the width of the image + * @param encoding the encoding + * @param height the height of the image + * @param bitmap the data used to store/encode the image + */ public static void apply( @NonNull WireBuffer buffer, int imageId, diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java index 5e5e5653053f..efd31afb1a05 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java @@ -24,11 +24,12 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent; import java.util.List; /** Add a click area to the document */ -public class ClickArea extends Operation implements RemoteComposeOperation { +public class ClickArea extends Operation implements RemoteComposeOperation, AccessibleComponent { private static final int OP_CODE = Operations.CLICK_AREA; private static final String CLASS_NAME = "ClickArea"; int mId; @@ -113,6 +114,11 @@ public class ClickArea extends Operation implements RemoteComposeOperation { return indent + toString(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -127,6 +133,21 @@ public class ClickArea extends Operation implements RemoteComposeOperation { return OP_CODE; } + @Override + public Integer getContentDescriptionId() { + return mContentDescription; + } + + /** + * @param buffer + * @param id + * @param contentDescription + * @param left + * @param top + * @param right + * @param bottom + * @param metadata + */ public static void apply( @NonNull WireBuffer buffer, int id, diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java index 2fe56d3ec935..b55f25c911fe 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java @@ -83,6 +83,11 @@ public class ClipPath extends PaintOperation { operations.add(op); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java index defa656b44e6..5a495d54e8da 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java @@ -28,8 +28,8 @@ import java.util.List; /** Support clip with a rectangle */ public class ClipRect extends DrawBase4 { - public static final int OP_CODE = Operations.CLIP_RECT; - public static final String CLASS_NAME = "ClipRect"; + private static final int OP_CODE = Operations.CLIP_RECT; + private static final String CLASS_NAME = "ClipRect"; /** * Read this operation and add it to the list of operations @@ -51,6 +51,11 @@ public class ClipRect extends DrawBase4 { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java index d86576dd99f2..68020157b8d1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java @@ -61,6 +61,11 @@ public class ColorConstant extends Operation { return "ColorConstant[" + mColorId + "] = " + Utils.colorInt(mColor) + ""; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java index 66f128f8f478..b385ecd9e5f7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java @@ -199,6 +199,11 @@ public class ColorExpression extends Operation implements VariableSupport { + ")"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java index 19c219bc0121..3e852364cfee 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java @@ -31,8 +31,8 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin import java.util.List; public class ComponentValue extends Operation implements SerializableToString { - public static final int OP_CODE = Operations.COMPONENT_VALUE; - public static final String CLASS_NAME = "ComponentValue"; + private static final int OP_CODE = Operations.COMPONENT_VALUE; + private static final String CLASS_NAME = "ComponentValue"; public static final int WIDTH = 0; public static final int HEIGHT = 1; @@ -50,6 +50,11 @@ public class ComponentValue extends Operation implements SerializableToString { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java index e888074cda74..ff85721027f7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java @@ -129,6 +129,11 @@ public class DataMapIds extends Operation { operations.add(data); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Encode a collection of name id pairs") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java index 3f95f02747f9..fd1f41065dd9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java @@ -26,8 +26,9 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; +/** Draw an Arc command the specified arc, will be scaled to fit inside the specified oval. */ public class DrawArc extends DrawBase6 { - public static final int OP_CODE = Operations.DRAW_ARC; + private static final int OP_CODE = Operations.DRAW_ARC; private static final String CLASS_NAME = "DrawArc"; /** @@ -114,8 +115,20 @@ public class DrawArc extends DrawBase6 { "Sweep angle (in degrees) measured clockwise"); } - public DrawArc(float v1, float v2, float v3, float v4, float v5, float v6) { - super(v1, v2, v3, v4, v5, v6); + /** + * Create Draw Arc command Draw the specified arc, which will be scaled to fit inside the + * specified oval. + * + * @param left the left side of the oval + * @param top the top of the oval + * @param right the right side of the oval + * @param bottom the bottom of the oval + * @param startAngle Starting angle (in degrees) where the arc begins + * @param sweepAngle Sweep angle (in degrees) measured clockwise + */ + public DrawArc( + float left, float top, float right, float bottom, float startAngle, float sweepAngle) { + super(left, top, right, bottom, startAngle, sweepAngle); mName = "DrawArc"; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java index 6c288a35621d..64c2730e5f9a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java @@ -145,6 +145,11 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor return null; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "DrawBase6"; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java index 69f5cc52a78a..cdb527dee460 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java @@ -118,6 +118,11 @@ public class DrawBitmap extends PaintOperation implements VariableSupport { operations.add(op); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java index 66646d7c2faa..638fe148d746 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java @@ -24,11 +24,12 @@ import com.android.internal.widget.remotecompose.core.PaintOperation; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent; import java.util.List; /** Operation to draw a given cached bitmap */ -public class DrawBitmapInt extends PaintOperation { +public class DrawBitmapInt extends PaintOperation implements AccessibleComponent { private static final int OP_CODE = Operations.DRAW_BITMAP_INT; private static final String CLASS_NAME = "DrawBitmapInt"; int mImageId; @@ -106,6 +107,16 @@ public class DrawBitmapInt extends PaintOperation { + ";"; } + @Override + public Integer getContentDescriptionId() { + return mContentDescId; + } + + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -170,6 +181,11 @@ public class DrawBitmapInt extends PaintOperation { operations.add(op); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Draw Operations", OP_CODE, CLASS_NAME) .description("Draw a bitmap using integer coordinates") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java index 170148608ab3..d6467c926747 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java @@ -27,11 +27,13 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; import com.android.internal.widget.remotecompose.core.operations.utilities.ImageScaling; +import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent; import java.util.List; /** Operation to draw a given cached bitmap */ -public class DrawBitmapScaled extends PaintOperation implements VariableSupport { +public class DrawBitmapScaled extends PaintOperation + implements VariableSupport, AccessibleComponent { private static final int OP_CODE = Operations.DRAW_BITMAP_SCALED; private static final String CLASS_NAME = "DrawBitmapScaled"; int mImageId; @@ -191,6 +193,16 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport + Utils.floatToString(mScaleFactor, mOutScaleFactor); } + @Override + public Integer getContentDescriptionId() { + return mContentDescId; + } + + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java index e6aecdbf8bbe..735e262ce94f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java @@ -50,6 +50,11 @@ public class DrawCircle extends DrawBase3 { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java index 04f32642d5fa..f3a190d98960 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java @@ -52,6 +52,11 @@ public class DrawLine extends DrawBase4 implements SerializableToString { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java index 0a50042b11c7..a009874302e0 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java @@ -50,6 +50,11 @@ public class DrawOval extends DrawBase4 { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java index 41b8243f070f..398cf4892e12 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java @@ -62,6 +62,11 @@ public class DrawPath extends PaintOperation { operations.add(op); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java index 7e22550d6544..38477ad0deb6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java @@ -51,6 +51,11 @@ public class DrawRect extends DrawBase4 { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java index 7616df09b6cc..51ece77a872a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java @@ -27,7 +27,7 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; public class DrawSector extends DrawBase6 { - public static final int OP_CODE = Operations.DRAW_SECTOR; + private static final int OP_CODE = Operations.DRAW_SECTOR; private static final String CLASS_NAME = "DrawSector"; /** diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java index 2c5d790b2f2a..8adba1d2616a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java @@ -121,6 +121,11 @@ public class DrawText extends PaintOperation implements VariableSupport { operations.add(op); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java index 7de52b8e5f3e..f839922b25e2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java @@ -130,6 +130,11 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport operations.add(op); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java index 18d9fdf1b671..86f3c992f2fb 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java @@ -98,6 +98,11 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport { operations.add(op); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "DrawTextOnPath"; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java index b83e4c2191b2..d4d4a5ecf6b9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java @@ -108,6 +108,11 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport { operations.add(op); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "DrawTweenPath"; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java index 7dd435a5c5b1..e04e691c312c 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java @@ -51,6 +51,11 @@ public class FloatConstant extends Operation { return "FloatConstant[" + mTextId + "] = " + mValue; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java index 3d92e129a236..c1872fd0fed0 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java @@ -139,6 +139,11 @@ public class FloatExpression extends Operation implements VariableSupport { } } + // Keep track of the last computed value when we are animated, + // e.g. if FloatAnimation or Spring is used, so that we can + // ask for a repaint. + float mLastAnimatedValue = Float.NaN; + @Override public void apply(@NonNull RemoteContext context) { updateVariables(context); @@ -146,12 +151,23 @@ public class FloatExpression extends Operation implements VariableSupport { if (Float.isNaN(mLastChange)) { mLastChange = t; } + float lastComputedValue; if (mFloatAnimation != null && !Float.isNaN(mLastCalculatedValue)) { float f = mFloatAnimation.get(t - mLastChange); context.loadFloat(mId, f); + lastComputedValue = f; + if (lastComputedValue != mLastAnimatedValue) { + mLastAnimatedValue = lastComputedValue; + context.needsRepaint(); + } } else if (mSpring != null) { float f = mSpring.get(t - mLastChange); context.loadFloat(mId, f); + lastComputedValue = f; + if (lastComputedValue != mLastAnimatedValue) { + mLastAnimatedValue = lastComputedValue; + context.needsRepaint(); + } } else { float v = mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length); @@ -205,6 +221,11 @@ public class FloatExpression extends Operation implements VariableSupport { + ")"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -236,6 +257,9 @@ public class FloatExpression extends Operation implements VariableSupport { buffer.writeInt(id); int len = value.length; + if (len > MAX_EXPRESSION_SIZE) { + throw new RuntimeException(AnimatedFloatExpression.toString(value, null) + " to long"); + } if (animation != null) { len |= (animation.length << 16); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java index 04e4346cf05d..656dc09c396f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java @@ -39,7 +39,7 @@ public class Header extends Operation implements RemoteComposeOperation { private static final int OP_CODE = Operations.HEADER; private static final String CLASS_NAME = "Header"; public static final int MAJOR_VERSION = 0; - public static final int MINOR_VERSION = 1; + public static final int MINOR_VERSION = 2; public static final int PATCH_VERSION = 0; int mMajorVersion; @@ -115,6 +115,11 @@ public class Header extends Operation implements RemoteComposeOperation { return toString(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java index 67274af7c283..f04f30dc62fb 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java @@ -136,6 +136,11 @@ public class IntegerExpression extends Operation implements VariableSupport { return "IntegerExpression[" + mId + "] = (" + s + ")"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java index aed597ae7494..044430d1e3c1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java @@ -26,6 +26,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio import java.util.List; +/** The restore previous matrix command */ public class MatrixRestore extends PaintOperation { private static final int OP_CODE = Operations.MATRIX_RESTORE; private static final String CLASS_NAME = "MatrixRestore"; @@ -54,6 +55,11 @@ public class MatrixRestore extends PaintOperation { return "MatrixRestore"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java index fece143ebfa1..57f5a0ebfab1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java @@ -26,8 +26,9 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; +/** The rotate the rendering command */ public class MatrixRotate extends DrawBase3 { - public static final int OP_CODE = Operations.MATRIX_ROTATE; + private static final int OP_CODE = Operations.MATRIX_ROTATE; private static final String CLASS_NAME = "MatrixRotate"; /** @@ -57,6 +58,11 @@ public class MatrixRotate extends DrawBase3 { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java index 7eb7b3ffaf34..aec316aea361 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java @@ -26,6 +26,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio import java.util.List; +/** The save the matrix state command */ public class MatrixSave extends PaintOperation { private static final int OP_CODE = Operations.MATRIX_SAVE; private static final String CLASS_NAME = "MatrixSave"; @@ -52,6 +53,11 @@ public class MatrixSave extends PaintOperation { operations.add(op); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java index 49bdd1b06eed..07f965f7d72a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java @@ -26,9 +26,10 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; +/** Scale the rendering matrix command */ public class MatrixScale extends DrawBase4 { - public static final int OP_CODE = Operations.MATRIX_SCALE; - public static final String CLASS_NAME = "MatrixScale"; + private static final int OP_CODE = Operations.MATRIX_SCALE; + private static final String CLASS_NAME = "MatrixScale"; /** * Read this operation and add it to the list of operations @@ -50,6 +51,11 @@ public class MatrixScale extends DrawBase4 { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java index 54b6fd1fa9ae..b31492d2cb57 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java @@ -27,9 +27,10 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio import java.util.List; +/** Skew the matrix command */ public class MatrixSkew extends DrawBase2 { - public static final int OP_CODE = Operations.MATRIX_SKEW; - public static final String CLASS_NAME = "MatrixSkew"; + private static final int OP_CODE = Operations.MATRIX_SKEW; + private static final String CLASS_NAME = "MatrixSkew"; /** * Read this operation and add it to the list of operations @@ -51,6 +52,11 @@ public class MatrixSkew extends DrawBase2 { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java index b57d83b770b2..11fa040183ce 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java @@ -26,9 +26,10 @@ import com.android.internal.widget.remotecompose.core.documentation.DocumentedOp import java.util.List; +/** translate the matrix command */ public class MatrixTranslate extends DrawBase2 { - public static final int OP_CODE = Operations.MATRIX_TRANSLATE; - public static final String CLASS_NAME = "MatrixTranslate"; + private static final int OP_CODE = Operations.MATRIX_TRANSLATE; + private static final String CLASS_NAME = "MatrixTranslate"; /** * Read this operation and add it to the list of operations @@ -50,6 +51,11 @@ public class MatrixTranslate extends DrawBase2 { return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java index 3c82f2b27ca6..dde632e0c346 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java @@ -64,6 +64,11 @@ public class NamedVariable extends Operation { + mVarType; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java index 3c0a842371c7..daf2c5502c5d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java @@ -61,6 +61,11 @@ public class PaintData extends PaintOperation implements VariableSupport { return "PaintData " + "\"" + mPaintData + "\""; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -92,6 +97,11 @@ public class PaintData extends PaintOperation implements VariableSupport { operations.add(data); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Data Operations", OP_CODE, CLASS_NAME) .description("Encode a Paint ") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java index 2b00001a521e..7ff879e41cac 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java @@ -109,6 +109,11 @@ public class PathAppend extends PaintOperation implements VariableSupport { public static final float CLOSE_NAN = Utils.asNan(CLOSE); public static final float DONE_NAN = Utils.asNan(DONE); + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -162,11 +167,12 @@ public class PathAppend extends PaintOperation implements VariableSupport { } @Override - public void paint(PaintContext context) {} + public void paint(PaintContext context) { + apply(context.getContext()); + } @Override public void apply(@NonNull RemoteContext context) { - updateVariables(context); float[] data = context.getPathData(mInstanceId); float[] out = mOutputPath; if (data != null) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java index b62f36b8db5f..75562cd8fb4c 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java @@ -48,6 +48,7 @@ public class PathCreate extends PaintOperation implements VariableSupport { @Override public void updateVariables(@NonNull RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { float v = mFloatPath[i]; if (Utils.isVariable(v)) { @@ -81,7 +82,19 @@ public class PathCreate extends PaintOperation implements VariableSupport { @NonNull @Override public String toString() { - return "PathCreate[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\""; + return "PathCreate[" + + mInstanceId + + "] = " + + "\"" + + deepToString(" ") + + "\"[" + + Utils.idStringFromNan(mFloatPath[1]) + + "] " + + mOutputPath[1] + + " [" + + Utils.idStringFromNan(mFloatPath[2]) + + "] " + + mOutputPath[2]; } public static final int MOVE = 10; @@ -99,6 +112,11 @@ public class PathCreate extends PaintOperation implements VariableSupport { public static final float CLOSE_NAN = Utils.asNan(CLOSE); public static final float DONE_NAN = Utils.asNan(DONE); + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -197,7 +215,9 @@ public class PathCreate extends PaintOperation implements VariableSupport { } @Override - public void paint(PaintContext context) {} + public void paint(PaintContext context) { + apply(context.getContext()); + } @Override public void apply(@NonNull RemoteContext context) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java index 4ec5436c8689..85a01fc7cbc7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java @@ -38,6 +38,7 @@ public class PathData extends Operation implements VariableSupport { int mInstanceId; float[] mFloatPath; float[] mOutputPath; + private boolean mPathChanged = true; PathData(int instanceId, float[] floatPath) { mInstanceId = instanceId; @@ -50,7 +51,11 @@ public class PathData extends Operation implements VariableSupport { for (int i = 0; i < mFloatPath.length; i++) { float v = mFloatPath[i]; if (Utils.isVariable(v)) { + float tmp = mOutputPath[i]; mOutputPath[i] = Float.isNaN(v) ? context.getFloat(Utils.idFromNan(v)) : v; + if (tmp != mOutputPath[i]) { + mPathChanged = true; + } } else { mOutputPath[i] = v; } @@ -107,6 +112,11 @@ public class PathData extends Operation implements VariableSupport { public static final float CLOSE_NAN = Utils.asNan(CLOSE); public static final float DONE_NAN = Utils.asNan(DONE); + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -216,6 +226,9 @@ public class PathData extends Operation implements VariableSupport { @Override public void apply(@NonNull RemoteContext context) { - context.loadPathData(mInstanceId, mOutputPath); + if (mPathChanged) { + context.loadPathData(mInstanceId, mOutputPath); + } + mPathChanged = false; } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java index a6fa680f647a..65adfeabefa6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java @@ -80,6 +80,11 @@ public class PathTween extends PaintOperation implements VariableSupport { + floatToString(mTween, mTweenOut); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java index aaa717629c2e..55dd88233265 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java @@ -200,6 +200,11 @@ public class RootContentBehavior extends Operation implements RemoteComposeOpera return toString(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -239,6 +244,11 @@ public class RootContentBehavior extends Operation implements RemoteComposeOpera operations.add(rootContentBehavior); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Protocol Operations", OP_CODE, CLASS_NAME) .description("Describes the behaviour of the root") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java index e92daa384dc3..ad86e0f2b1f3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java @@ -24,11 +24,13 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent; import java.util.List; /** Describe a content description for the document */ -public class RootContentDescription extends Operation implements RemoteComposeOperation { +public class RootContentDescription extends Operation + implements RemoteComposeOperation, AccessibleComponent { private static final int OP_CODE = Operations.ROOT_CONTENT_DESCRIPTION; private static final String CLASS_NAME = "RootContentDescription"; int mContentDescription; @@ -43,6 +45,11 @@ public class RootContentDescription extends Operation implements RemoteComposeOp } @Override + public boolean isInterestingForSemantics() { + return mContentDescription != 0; + } + + @Override public void write(@NonNull WireBuffer buffer) { apply(buffer, mContentDescription); } @@ -64,6 +71,16 @@ public class RootContentDescription extends Operation implements RemoteComposeOp return toString(); } + @Override + public Integer getContentDescriptionId() { + return mContentDescription; + } + + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java index e2502feb2bb1..8e4098ed51f9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java @@ -198,6 +198,11 @@ public class ShaderData extends Operation implements VariableSupport { } } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java index 3f679bf47582..d48de37996ee 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java @@ -54,6 +54,11 @@ public class TextData extends Operation implements SerializableToString { return "TextData[" + mTextId + "] = \"" + Utils.trimString(mText, 10) + "\""; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java index 4d01e0c3cbe4..cc0ff025f09b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java @@ -122,6 +122,11 @@ public class TextFromFloat extends Operation implements VariableSupport { } } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java index 3ec6f019c358..dceb8b67ec3a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java @@ -79,6 +79,11 @@ public class TextLookup extends Operation implements VariableSupport { } } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java index 9c0ee535f62a..823b70656c86 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java @@ -72,6 +72,11 @@ public class TextLookupInt extends Operation implements VariableSupport { context.listensTo(mIndex, this); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java index 5b0c38fe996b..d69561566b56 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java @@ -53,6 +53,11 @@ public class TextMerge extends Operation { return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java index e329c38daf20..6c9105dbadfe 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java @@ -72,6 +72,11 @@ public class Theme extends Operation implements RemoteComposeOperation { return indent + toString(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java index e2e20bc5e57f..f42abfcd1671 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java @@ -21,6 +21,7 @@ import static com.android.internal.widget.remotecompose.core.documentation.Docum import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT; import android.annotation.NonNull; +import android.annotation.Nullable; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; @@ -30,6 +31,7 @@ import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.operations.layout.Component; +import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent; import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression; import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap; import com.android.internal.widget.remotecompose.core.operations.utilities.touch.VelocityEasing; @@ -80,14 +82,41 @@ public class TouchExpression extends Operation implements VariableSupport, Touch int mTouchEffects; float mVelocityId; + /** Stop with some deceleration */ public static final int STOP_GENTLY = 0; + + /** Stop only at the start or end */ public static final int STOP_ENDS = 2; + + /** Stop on touch up */ public static final int STOP_INSTANTLY = 1; + + /** Stop at evenly spaced notches */ public static final int STOP_NOTCHES_EVEN = 3; + + /** Stop at a collection points described in percents of the range */ public static final int STOP_NOTCHES_PERCENTS = 4; + + /** Stop at a collectiond of point described in abslute cordnates */ public static final int STOP_NOTCHES_ABSOLUTE = 5; + + /** Jump to the absloute poition of the point */ public static final int STOP_ABSOLUTE_POS = 6; + /** + * create a touch expression + * + * @param id The float id the value is output to + * @param exp the expression (containing TOUCH_* ) + * @param defValue the default value + * @param min the minimum value + * @param max the maximum value + * @param touchEffects the type of touch mode + * @param velocityId the valocity (not used) + * @param stopMode the behavour on touch oup + * @param stopSpec the paraameters that affect the touch up behavour + * @param easingSpec the easing parameters for coming to a stop + */ public TouchExpression( int id, float[] exp, @@ -129,7 +158,6 @@ public class TouchExpression extends Operation implements VariableSupport, Touch @Override public void updateVariables(RemoteContext context) { - if (mPreCalcValue == null || mPreCalcValue.length != mSrcExp.length) { mPreCalcValue = new float[mSrcExp.length]; } @@ -192,7 +220,9 @@ public class TouchExpression extends Operation implements VariableSupport, Touch if (Float.isNaN(mDefValue)) { context.listensTo(Utils.idFromNan(mDefValue), this); } - context.addTouchListener(this); + if (mComponent == null) { + context.addTouchListener(this); + } for (float v : mSrcExp) { if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v) @@ -332,9 +362,25 @@ public class TouchExpression extends Operation implements VariableSupport, Touch float mScrLeft, mScrRight, mScrTop, mScrBottom; - @Override - public void apply(RemoteContext context) { - Component comp = context.mLastComponent; + @Nullable Component mComponent; + + /** + * Set the component the touch expression is in (if any) + * @param component the component, or null if outside + */ + public void setComponent(@Nullable Component component) { + mComponent = component; + if (mComponent != null) { + try { + RootLayoutComponent root = mComponent.getRoot(); + root.setHasTouchListeners(true); + } catch (Exception e) { + } + } + } + + private void updateBounds() { + Component comp = mComponent; if (comp != null) { float x = comp.getX(); float y = comp.getY(); @@ -351,7 +397,11 @@ public class TouchExpression extends Operation implements VariableSupport, Touch mScrRight = w + x; mScrBottom = h + y; } - updateVariables(context); + } + + @Override + public void apply(RemoteContext context) { + updateBounds(); if (mUnmodified) { mCurrentValue = mOutDefValue; context.loadFloat(mId, wrap(mCurrentValue)); @@ -371,6 +421,7 @@ public class TouchExpression extends Operation implements VariableSupport, Touch mEasingToStop = false; } crossNotchCheck(context); + context.needsRepaint(); return; } if (mTouchDown) { @@ -395,11 +446,11 @@ public class TouchExpression extends Operation implements VariableSupport, Touch @Override public void touchDown(RemoteContext context, float x, float y) { - if (!(x >= mScrLeft && x <= mScrRight && y >= mScrTop && y <= mScrBottom)) { Utils.log("NOT IN WINDOW " + x + ", " + y + " " + mScrLeft + ", " + mScrTop); return; } + mEasingToStop = false; mTouchDown = true; mUnmodified = false; if (mMode == 0) { @@ -407,6 +458,7 @@ public class TouchExpression extends Operation implements VariableSupport, Touch mDownTouchValue = mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length); } + context.needsRepaint(); } @Override @@ -441,6 +493,7 @@ public class TouchExpression extends Operation implements VariableSupport, Touch float time = mMaxTime * Math.abs(dest - value) / (2 * mMaxVelocity); mEasyTouch.config(value, dest, slope, time, mMaxAcceleration, mMaxVelocity, null); mEasingToStop = true; + context.needsRepaint(); } @Override @@ -449,7 +502,7 @@ public class TouchExpression extends Operation implements VariableSupport, Touch return; } apply(context); - context.getDocument().getRootLayoutComponent().needsRepaint(); + context.needsRepaint(); } @Override @@ -494,6 +547,12 @@ public class TouchExpression extends Operation implements VariableSupport, Touch // ===================== static ====================== + /** + * The name of the class + * + * @return the name + */ + @NonNull public static String name() { return CLASS_NAME; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java index 0f840597e3c6..1c241601765b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java @@ -23,8 +23,23 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin /** Operations representing actions on the document */ public interface ActionOperation { + /** + * Serialize the string + * + * @param indent padding to display + * @param serializer append the string + */ void serializeToString(int indent, @NonNull StringSerializer serializer); + /** + * Run the action + * + * @param context remote context + * @param document document + * @param component component + * @param x the x location of the action + * @param y the y location of the action + */ void runAction( @NonNull RemoteContext context, @NonNull CoreDocument document, diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java index 19f4c2b04956..652ab2bc1cbc 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java @@ -20,6 +20,7 @@ import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; import com.android.internal.widget.remotecompose.core.operations.utilities.easing.GeneralEasing; +/** Value animation for layouts */ public class AnimatableValue { boolean mIsVariable = false; int mId = 0; @@ -34,6 +35,11 @@ public class AnimatableValue { int mMotionEasingType = GeneralEasing.CUBIC_STANDARD; FloatAnimation mMotionEasing; + /** + * Value to animate + * + * @param value value + */ public AnimatableValue(float value) { if (Utils.isVariable(value)) { mId = Utils.idFromNan(value); @@ -43,10 +49,21 @@ public class AnimatableValue { } } + /** + * Get the value + * + * @return the value + */ public float getValue() { return mValue; } + /** + * Evaluate going through FloatAnimation if needed + * + * @param context the paint context + * @return the current value + */ public float evaluate(PaintContext context) { if (!mIsVariable) { return mValue; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java index 121b18014a21..34b7a2326a21 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java @@ -41,6 +41,11 @@ public class CanvasContent extends Component implements ComponentStartOperation super(parent, componentId, animationId, x, y, width, height); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "CanvasContent"; @@ -77,6 +82,11 @@ public class CanvasContent extends Component implements ComponentStartOperation operations.add(new CanvasContent(componentId, 0, 0, 0, 0, null, -1)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .field(INT, "COMPONENT_ID", "unique id for this component") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java index 34c42494d964..dcf1d250b2f5 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java @@ -16,6 +16,7 @@ package com.android.internal.widget.remotecompose.core.operations.layout; import android.annotation.NonNull; +import android.annotation.Nullable; import com.android.internal.widget.remotecompose.core.CoreDocument; import com.android.internal.widget.remotecompose.core.Operation; @@ -33,13 +34,15 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Color import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; import com.android.internal.widget.remotecompose.core.operations.utilities.easing.Easing; import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; +import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent; +import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics; import java.util.ArrayList; import java.util.List; /** Represents a click modifier + actions */ public class ClickModifierOperation extends PaintOperation - implements ModifierOperation, DecoratorComponent, ClickHandler { + implements ModifierOperation, DecoratorComponent, ClickHandler, AccessibleComponent { private static final int OP_CODE = Operations.MODIFIER_CLICK; long mAnimateRippleStart = 0; @@ -54,6 +57,22 @@ public class ClickModifierOperation extends PaintOperation @NonNull PaintBundle mPaint = new PaintBundle(); + @Override + public boolean isClickable() { + return true; + } + + @Nullable + @Override + public Role getRole() { + return Role.BUTTON; + } + + @Override + public CoreSemantics.Mode getMode() { + return CoreSemantics.Mode.MERGE; + } + public void animateRipple(float x, float y) { mAnimateRippleStart = System.currentTimeMillis(); mAnimateRippleX = x; @@ -80,6 +99,10 @@ public class ClickModifierOperation extends PaintOperation @Override public void apply(@NonNull RemoteContext context) { + RootLayoutComponent root = context.getDocument().getRootLayoutComponent(); + if (root != null) { + root.setHasTouchListeners(true); + } for (Operation op : mList) { if (op instanceof TextData) { op.apply(context); @@ -136,7 +159,8 @@ public class ClickModifierOperation extends PaintOperation } @Override - public void layout(@NonNull RemoteContext context, float width, float height) { + public void layout( + @NonNull RemoteContext context, Component component, float width, float height) { mWidth = width; mHeight = height; } @@ -173,6 +197,11 @@ public class ClickModifierOperation extends PaintOperation context.hapticEffect(3); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "ClickModifier"; @@ -192,6 +221,11 @@ public class ClickModifierOperation extends PaintOperation operations.add(new ClickModifierOperation()); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", OP_CODE, name()) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java index faa259f6b835..e95dfdaa4cf9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java @@ -28,6 +28,7 @@ import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.operations.ComponentValue; import com.android.internal.widget.remotecompose.core.operations.TextData; +import com.android.internal.widget.remotecompose.core.operations.TouchExpression; import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimateMeasure; import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec; import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure; @@ -119,6 +120,17 @@ public class Component extends PaintOperation implements Measurable, Serializabl mHeight = value; } + @Override + public void apply(@NonNull RemoteContext context) { + for (Operation op : mList) { + if (op instanceof VariableSupport && op.isDirty()) { + op.markNotDirty(); + ((VariableSupport) op).updateVariables(context); + } + } + super.apply(context); + } + /** * Utility function to update variables referencing this component dimensions * @@ -233,14 +245,6 @@ public class Component extends PaintOperation implements Measurable, Serializabl if (!mComponentValues.isEmpty()) { updateComponentValues(context); } - for (Operation o : mList) { - if (o instanceof Component) { - ((Component) o).updateVariables(context); - } - if (o instanceof VariableSupport) { - o.apply(context); - } - } context.mLastComponent = prev; } @@ -248,14 +252,40 @@ public class Component extends PaintOperation implements Measurable, Serializabl mComponentValues.add(v); } - public float intrinsicWidth() { + /** + * Returns the intrinsic width of the layout + * + * @param context + * @return the width in pixels + */ + public float intrinsicWidth(@Nullable RemoteContext context) { return getWidth(); } - public float intrinsicHeight() { + /** + * Returns the intrinsic height of the layout + * + * @param context + * @return the height in pixels + */ + public float intrinsicHeight(@Nullable RemoteContext context) { return getHeight(); } + /** + * This function is called after a component is created, with its mList initialized. This let + * the component a chance to do some post-initialization work on its children ops. + */ + public void inflate() { + for (Operation op : mList) { + if (op instanceof TouchExpression) { + // Make sure to set the component of a touch expression that belongs to us! + TouchExpression touchExpression = (TouchExpression) op; + touchExpression.setComponent(this); + } + } + } + public enum Visibility { GONE, VISIBLE, @@ -409,11 +439,23 @@ public class Component extends PaintOperation implements Measurable, Serializabl if (op instanceof TouchHandler) { ((TouchHandler) op).onTouchDown(context, document, this, cx, cy); } + if (op instanceof TouchExpression) { + TouchExpression touchExpression = (TouchExpression) op; + touchExpression.updateVariables(context); + touchExpression.touchDown(context, cx, cy); + document.appliedTouchOperation(this); + } } } public void onTouchUp( - RemoteContext context, CoreDocument document, float x, float y, boolean force) { + RemoteContext context, + CoreDocument document, + float x, + float y, + float dx, + float dy, + boolean force) { if (!force && !contains(x, y)) { return; } @@ -421,10 +463,15 @@ public class Component extends PaintOperation implements Measurable, Serializabl float cy = y - getScrollY(); for (Operation op : mList) { if (op instanceof Component) { - ((Component) op).onTouchUp(context, document, cx, cy, force); + ((Component) op).onTouchUp(context, document, cx, cy, dx, dy, force); } if (op instanceof TouchHandler) { - ((TouchHandler) op).onTouchUp(context, document, this, cx, cy); + ((TouchHandler) op).onTouchUp(context, document, this, cx, cy, dx, dy); + } + if (op instanceof TouchExpression) { + TouchExpression touchExpression = (TouchExpression) op; + touchExpression.updateVariables(context); + touchExpression.touchUp(context, cx, cy, dx, dy); } } } @@ -443,6 +490,11 @@ public class Component extends PaintOperation implements Measurable, Serializabl if (op instanceof TouchHandler) { ((TouchHandler) op).onTouchCancel(context, document, this, cx, cy); } + if (op instanceof TouchExpression) { + TouchExpression touchExpression = (TouchExpression) op; + touchExpression.updateVariables(context); + touchExpression.touchUp(context, cx, cy, 0, 0); + } } } @@ -460,6 +512,11 @@ public class Component extends PaintOperation implements Measurable, Serializabl if (op instanceof TouchHandler) { ((TouchHandler) op).onTouchDrag(context, document, this, cx, cy); } + if (op instanceof TouchExpression) { + TouchExpression touchExpression = (TouchExpression) op; + touchExpression.updateVariables(context); + touchExpression.touchDrag(context, x, y); + } } } @@ -641,6 +698,9 @@ public class Component extends PaintOperation implements Measurable, Serializabl } public void paintingComponent(@NonNull PaintContext context) { + if (!mComponentValues.isEmpty()) { + updateComponentValues(context.getContext()); + } if (mPreTranslate != null) { mPreTranslate.paint(context); } @@ -652,7 +712,15 @@ public class Component extends PaintOperation implements Measurable, Serializabl debugBox(this, context); } for (Operation op : mList) { - op.apply(context.getContext()); + if (op.isDirty() && op instanceof VariableSupport) { + ((VariableSupport) op).updateVariables(context.getContext()); + op.markNotDirty(); + } + if (op instanceof PaintOperation) { + ((PaintOperation) op).paint(context); + } else { + op.apply(context.getContext()); + } } context.restore(); context.getContext().mLastComponent = prev; @@ -661,7 +729,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl public boolean applyAnimationAsNeeded(@NonNull PaintContext context) { if (context.isAnimationEnabled() && mAnimateMeasure != null) { mAnimateMeasure.apply(context); - needsRepaint(); + context.needsRepaint(); return true; } return false; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java index 396644c45fa4..5da06634d101 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java @@ -49,6 +49,11 @@ public class ComponentEnd extends Operation { return (indent != null ? indent : "") + toString(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "ComponentEnd"; @@ -81,6 +86,11 @@ public class ComponentEnd extends Operation { operations.add(new ComponentEnd()); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java index a85ae270ffb1..4349b31d76e3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java @@ -157,6 +157,11 @@ public class ComponentStart extends Operation implements ComponentStartOperation } } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "ComponentStart"; @@ -198,6 +203,11 @@ public class ComponentStart extends Operation implements ComponentStartOperation operations.add(new ComponentStart(type, componentId, width, height)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java index d6170074238a..9ca2f2ed3ba7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java @@ -24,5 +24,13 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; * measured. Eg borders, background, clips, etc. */ public interface DecoratorComponent { - void layout(@NonNull RemoteContext context, float width, float height); + /** + * Layout the decorator + * + * @param context + * @param component the associated component + * @param width horizontal dimension in pixels + * @param height vertical dimension in pixels + */ + void layout(@NonNull RemoteContext context, Component component, float width, float height); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java index 7b0e4a2e2627..e25392c5d2ff 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.OperationInterface; import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.operations.BitmapData; import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; @@ -36,6 +38,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation; +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation; @@ -54,6 +57,12 @@ public class LayoutComponent extends Component { protected float mPaddingTop = 0f; protected float mPaddingBottom = 0f; + float mScrollX = 0f; + float mScrollY = 0f; + + @Nullable protected ScrollDelegate mHorizontalScrollDelegate = null; + @Nullable protected ScrollDelegate mVerticalScrollDelegate = null; + @NonNull protected ComponentModifiers mComponentModifiers = new ComponentModifiers(); @NonNull @@ -111,6 +120,7 @@ public class LayoutComponent extends Component { // Should be removed after ImageLayout is in private static final boolean USE_IMAGE_TEMP_FIX = true; + @Override public void inflate() { ArrayList<TextData> data = new ArrayList<>(); ArrayList<Operation> supportedOperations = new ArrayList<>(); @@ -144,6 +154,7 @@ public class LayoutComponent extends Component { if (!canvasContent.mList.isEmpty()) { mContent.mList.clear(); mChildrenComponents.add(canvasContent); + canvasContent.inflate(); } } else { content.getData(data); @@ -155,6 +166,9 @@ public class LayoutComponent extends Component { if (op instanceof ComponentVisibilityOperation) { ((ComponentVisibilityOperation) op).setParent(this); } + if (op instanceof ScrollModifierOperation) { + ((ScrollModifierOperation) op).inflate(this); + } mComponentModifiers.add((ModifierOperation) op); } else if (op instanceof TextData) { data.add((TextData) op); @@ -162,6 +176,9 @@ public class LayoutComponent extends Component { || (op instanceof PaintData) || (op instanceof FloatExpression)) { supportedOperations.add(op); + if (op instanceof TouchExpression) { + ((TouchExpression) op).setComponent(this); + } } else { // nothing } @@ -186,8 +203,6 @@ public class LayoutComponent extends Component { mPaddingRight = 0f; mPaddingBottom = 0f; - boolean applyHorizontalMargin = true; - boolean applyVerticalMargin = true; for (OperationInterface op : mComponentModifiers.getList()) { if (op instanceof PaddingModifierOperation) { // We are accumulating padding modifiers to compute the margin @@ -209,6 +224,14 @@ public class LayoutComponent extends Component { mZIndexModifier = (ZIndexModifierOperation) op; } else if (op instanceof GraphicsLayerModifierOperation) { mGraphicsLayerModifier = (GraphicsLayerModifierOperation) op; + } else if (op instanceof ScrollDelegate) { + ScrollDelegate scrollDelegate = (ScrollDelegate) op; + if (scrollDelegate.handlesHorizontalScroll()) { + mHorizontalScrollDelegate = scrollDelegate; + } + if (scrollDelegate.handlesVerticalScroll()) { + mVerticalScrollDelegate = scrollDelegate; + } } } if (mWidthModifier == null) { @@ -217,8 +240,8 @@ public class LayoutComponent extends Component { if (mHeightModifier == null) { mHeightModifier = new HeightModifierOperation(DimensionModifierOperation.Type.WRAP); } - setWidth(computeModifierDefinedWidth()); - setHeight(computeModifierDefinedHeight()); + setWidth(computeModifierDefinedWidth(null)); + setHeight(computeModifierDefinedHeight(null)); } @NonNull @@ -228,13 +251,36 @@ public class LayoutComponent extends Component { } @Override + public void getLocationInWindow(@NonNull float[] value) { + value[0] += mX + mPaddingLeft; + value[1] += mY + mPaddingTop; + if (mParent != null) { + mParent.getLocationInWindow(value); + } + } + + @Override public float getScrollX() { - return mComponentModifiers.getScrollX(); + if (mHorizontalScrollDelegate != null) { + return mHorizontalScrollDelegate.getScrollX(mScrollX); + } + return mScrollX; + } + + public void setScrollX(float value) { + mScrollX = value; } @Override public float getScrollY() { - return mComponentModifiers.getScrollY(); + if (mVerticalScrollDelegate != null) { + return mVerticalScrollDelegate.getScrollY(mScrollY); + } + return mScrollY; + } + + public void setScrollY(float value) { + mScrollY = value; } @Override @@ -279,10 +325,18 @@ public class LayoutComponent extends Component { ArrayList<Component> sorted = new ArrayList<Component>(mChildrenComponents); sorted.sort((a, b) -> (int) (a.getZIndex() - b.getZIndex())); for (Component child : sorted) { + if (child.isDirty() && child instanceof VariableSupport) { + child.updateVariables(context.getContext()); + child.markNotDirty(); + } child.paint(context); } } else { for (Component child : mChildrenComponents) { + if (child.isDirty() && child instanceof VariableSupport) { + child.updateVariables(context.getContext()); + child.markNotDirty(); + } child.paint(context); } } @@ -295,11 +349,15 @@ public class LayoutComponent extends Component { } /** Traverse the modifiers to compute indicated dimension */ - public float computeModifierDefinedWidth() { + public float computeModifierDefinedWidth(@Nullable RemoteContext context) { float s = 0f; float e = 0f; float w = 0f; for (OperationInterface c : mComponentModifiers.getList()) { + if (context != null && c.isDirty() && c instanceof VariableSupport) { + ((VariableSupport) c).updateVariables(context); + c.markNotDirty(); + } if (c instanceof WidthModifierOperation) { WidthModifierOperation o = (WidthModifierOperation) c; if (o.getType() == DimensionModifierOperation.Type.EXACT @@ -339,11 +397,15 @@ public class LayoutComponent extends Component { } /** Traverse the modifiers to compute indicated dimension */ - public float computeModifierDefinedHeight() { + public float computeModifierDefinedHeight(@Nullable RemoteContext context) { float t = 0f; float b = 0f; float h = 0f; for (OperationInterface c : mComponentModifiers.getList()) { + if (context != null && c.isDirty() && c instanceof VariableSupport) { + ((VariableSupport) c).updateVariables(context); + c.markNotDirty(); + } if (c instanceof HeightModifierOperation) { HeightModifierOperation o = (HeightModifierOperation) c; if (o.getType() == DimensionModifierOperation.Type.EXACT @@ -383,6 +445,11 @@ public class LayoutComponent extends Component { } @NonNull + public ComponentModifiers getComponentModifiers() { + return mComponentModifiers; + } + + @NonNull public ArrayList<Component> getChildrenComponents() { return mChildrenComponents; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java index 20e4688aaa32..9bfbe6a42a37 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java @@ -41,6 +41,11 @@ public class LayoutComponentContent extends Component implements ComponentStartO super(parent, componentId, animationId, x, y, width, height); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "LayoutContent"; @@ -77,6 +82,11 @@ public class LayoutComponentContent extends Component implements ComponentStartO operations.add(new LayoutComponentContent(componentId, 0, 0, 0, 0, null, -1)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .field(INT, "COMPONENT_ID", "unique id for this component") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java index df960e45736e..505656e212f1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java @@ -32,8 +32,8 @@ public abstract class ListActionsOperation extends PaintOperation implements ModifierOperation, DecoratorComponent { String mOperationName; - float mWidth = 0; - float mHeight = 0; + protected float mWidth = 0; + protected float mHeight = 0; private final float[] mLocationInWindow = new float[2]; @@ -71,7 +71,7 @@ public abstract class ListActionsOperation extends PaintOperation public void paint(PaintContext context) {} @Override - public void layout(RemoteContext context, float width, float height) { + public void layout(RemoteContext context, Component component, float width, float height) { mWidth = width; mHeight = height; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java index d88f711c62c6..3d389e5badef 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java @@ -49,6 +49,11 @@ public class LoopEnd extends Operation { return (indent != null ? indent : "") + toString(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "LoopEnd"; @@ -77,6 +82,11 @@ public class LoopEnd extends Operation { operations.add(new LoopEnd()); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Operations", id(), name()).description("End tag for loops"); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java index 83a2f0e1ffa3..1b85681bcc08 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java @@ -117,7 +117,7 @@ public class LoopOperation extends PaintOperation implements VariableSupport { for (float i = mFromOut; i < mUntilOut; i += mStepOut) { context.getContext().loadFloat(mIndexVariableId, i); for (Operation op : mList) { - if (op instanceof VariableSupport) { + if (op instanceof VariableSupport && op.isDirty()) { ((VariableSupport) op).updateVariables(context.getContext()); } op.apply(context.getContext()); @@ -126,6 +126,11 @@ public class LoopOperation extends PaintOperation implements VariableSupport { } } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "Loop"; @@ -154,6 +159,11 @@ public class LoopOperation extends PaintOperation implements VariableSupport { operations.add(new LoopOperation(indexId, from, step, until)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Operations", OP_CODE, name()) .description("Loop. This operation execute" + " a list of action in a loop") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java index 99b7e68786fb..12a673d7380f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java @@ -49,6 +49,11 @@ public class OperationsListEnd extends Operation { return (indent != null ? indent : "") + toString(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "ListEnd"; @@ -77,6 +82,11 @@ public class OperationsListEnd extends Operation { operations.add(new OperationsListEnd()); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description("End tag for list of operations."); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java index fd1628729dd4..11c0f3f394f5 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java @@ -192,6 +192,11 @@ public class RootLayoutComponent extends Component implements ComponentStartOper } } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "RootLayout"; @@ -222,6 +227,11 @@ public class RootLayoutComponent extends Component implements ComponentStartOper operations.add(new RootLayoutComponent(componentId, 0, 0, 0, 0, null, -1)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .field(INT, "COMPONENT_ID", "unique id for this component") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ScrollDelegate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ScrollDelegate.java new file mode 100644 index 000000000000..7ef9766ba077 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ScrollDelegate.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.layout; + +/** + * Represent scroll delegates components. + * + * <p>Components have scroll X & Y properties. We can inject a scroll delegate as a modifier (e.g. a + * scrollView, a marquee...) to control the value of those properties. + */ +public interface ScrollDelegate { + + /** + * Returns the horizontal scroll value + * + * @param currentValue the current value + * @return the value set by the delegate + */ + float getScrollX(float currentValue); + + /** + * Returns the vertical scroll value + * + * @param currentValue the current value + * @return the value set by the delegate + */ + float getScrollY(float currentValue); + + /** + * Returns true if the delegate can handle horizontal scroll + * + * @return true if the delegate handles horizontal scrolling + */ + boolean handlesHorizontalScroll(); + + /** + * Returns true if the delegate can handle vertical scroll + * + * @return true if the delegate handles vertical scrolling + */ + boolean handlesVerticalScroll(); + + /** Reset the delegate (e.g. the content of the component has changed) */ + void reset(); +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java index 3185bb5f0f72..4977a15e2dc1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java @@ -15,6 +15,8 @@ */ package com.android.internal.widget.remotecompose.core.operations.layout; +import android.annotation.NonNull; + import com.android.internal.widget.remotecompose.core.CoreDocument; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; @@ -60,7 +62,13 @@ public class TouchCancelModifierOperation extends ListActionsOperation implement @Override public void onTouchUp( - RemoteContext context, CoreDocument document, Component component, float x, float y) { + RemoteContext context, + CoreDocument document, + Component component, + float x, + float y, + float dx, + float dy) { // nothing } @@ -76,6 +84,12 @@ public class TouchCancelModifierOperation extends ListActionsOperation implement // nothing } + /** + * The name of the class + * + * @return the name + */ + @NonNull public static String name() { return "TouchCancelModifier"; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java index d98911f82060..8c51f2eac383 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java @@ -15,6 +15,8 @@ */ package com.android.internal.widget.remotecompose.core.operations.layout; +import android.annotation.NonNull; + import com.android.internal.widget.remotecompose.core.CoreDocument; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; @@ -62,7 +64,13 @@ public class TouchDownModifierOperation extends ListActionsOperation implements @Override public void onTouchUp( - RemoteContext context, CoreDocument document, Component component, float x, float y) { + RemoteContext context, + CoreDocument document, + Component component, + float x, + float y, + float dx, + float dy) { // nothing } @@ -78,6 +86,12 @@ public class TouchDownModifierOperation extends ListActionsOperation implements // nothing } + /** + * The name of the class + * + * @return the name + */ + @NonNull public static String name() { return "TouchModifier"; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchHandler.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchHandler.java index ac9dd908d6a4..607060e51496 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchHandler.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchHandler.java @@ -41,9 +41,17 @@ public interface TouchHandler { * @param component the component on which the touch has been received * @param x the x position of the click in document coordinates * @param y the y position of the click in document coordinates + * @param dx + * @param dy */ void onTouchUp( - RemoteContext context, CoreDocument document, Component component, float x, float y); + RemoteContext context, + CoreDocument document, + Component component, + float x, + float y, + float dx, + float dy); /** * callback for a touch move event diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java index f6cb3750906f..a12c356f7c48 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java @@ -15,6 +15,8 @@ */ package com.android.internal.widget.remotecompose.core.operations.layout; +import android.annotation.NonNull; + import com.android.internal.widget.remotecompose.core.CoreDocument; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; @@ -60,7 +62,13 @@ public class TouchUpModifierOperation extends ListActionsOperation implements To @Override public void onTouchUp( - RemoteContext context, CoreDocument document, Component component, float x, float y) { + RemoteContext context, + CoreDocument document, + Component component, + float x, + float y, + float dx, + float dy) { applyActions(context, document, component, x, y, true); } @@ -76,6 +84,12 @@ public class TouchUpModifierOperation extends ListActionsOperation implements To // nothing } + /** + * The name of the class + * + * @return the name + */ + @NonNull public static String name() { return "TouchUpModifier"; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java index b3430998a520..2af3c739035c 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java @@ -120,7 +120,7 @@ public class AnimateMeasure { h -= pop.getTop() + pop.getBottom(); } if (op instanceof DecoratorComponent) { - ((DecoratorComponent) op).layout(context.getContext(), w, h); + ((DecoratorComponent) op).layout(context.getContext(), mComponent, w, h); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java index b230b09112b2..6dff4a87088b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java @@ -137,6 +137,11 @@ public class AnimationSpec extends Operation { return (indent != null ? indent : "") + toString(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "AnimationSpec"; @@ -224,6 +229,11 @@ public class AnimationSpec extends Operation { operations.add(op); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description("define the animation") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java index 01cd7ccd0b94..8076cb10ea0c 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java @@ -119,8 +119,9 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation size.setHeight(Math.max(size.getHeight(), m.getH())); } // add padding - size.setWidth(Math.max(size.getWidth(), computeModifierDefinedWidth())); - size.setHeight(Math.max(size.getHeight(), computeModifierDefinedHeight())); + size.setWidth(Math.max(size.getWidth(), computeModifierDefinedWidth(context.getContext()))); + size.setHeight( + Math.max(size.getHeight(), computeModifierDefinedHeight(context.getContext()))); } @Override @@ -172,6 +173,11 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation } } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "BoxLayout"; @@ -219,6 +225,11 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation verticalPositioning)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java index 665db2637674..0091a47eebfb 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java @@ -72,6 +72,11 @@ public class CanvasLayout extends BoxLayout { return "CANVAS"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "CanvasLayout"; @@ -104,6 +109,11 @@ public class CanvasLayout extends BoxLayout { operations.add(new CanvasLayout(null, componentId, animationId)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description("Canvas implementation. Encapsulate draw operations.\n\n") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java index 5b9ee0ff511f..249e84a1c1bc 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.operations.layout.Component; @@ -169,11 +170,11 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati } @Override - public float intrinsicHeight() { - float height = computeModifierDefinedHeight(); + public float intrinsicHeight(@NonNull RemoteContext context) { + float height = computeModifierDefinedHeight(context); float componentHeights = 0f; for (Component c : mChildrenComponents) { - componentHeights += c.intrinsicHeight(); + componentHeights += c.intrinsicHeight(context); } return Math.max(height, componentHeights); } @@ -341,6 +342,11 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati DebugLog.e(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "ColumnLayout"; @@ -392,6 +398,11 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati spacedBy)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java index 6a15b7f1b178..a5edaa8de3af 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java @@ -61,19 +61,19 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl } @Override - public float intrinsicHeight() { - float height = computeModifierDefinedHeight(); + public float intrinsicHeight(@Nullable RemoteContext context) { + float height = computeModifierDefinedHeight(context); for (Component c : mChildrenComponents) { - height = Math.max(c.intrinsicHeight(), height); + height = Math.max(c.intrinsicHeight(context), height); } return height; } @Override - public float intrinsicWidth() { - float width = computeModifierDefinedWidth(); + public float intrinsicWidth(@Nullable RemoteContext context) { + float width = computeModifierDefinedWidth(context); for (Component c : mChildrenComponents) { - width = Math.max(c.intrinsicWidth(), width); + width = Math.max(c.intrinsicWidth(context), width); } return width; } @@ -132,16 +132,17 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl @NonNull MeasurePass measure) { boolean hasWrap = true; - float measuredWidth = Math.min(maxWidth, computeModifierDefinedWidth()); - float measuredHeight = Math.min(maxHeight, computeModifierDefinedHeight()); + float measuredWidth = Math.min(maxWidth, computeModifierDefinedWidth(context.getContext())); + float measuredHeight = + Math.min(maxHeight, computeModifierDefinedHeight(context.getContext())); float insetMaxWidth = maxWidth - mPaddingLeft - mPaddingRight; float insetMaxHeight = maxHeight - mPaddingTop - mPaddingBottom; if (mWidthModifier.isIntrinsicMin()) { - maxWidth = intrinsicWidth(); + maxWidth = intrinsicWidth(context.getContext()); } if (mHeightModifier.isIntrinsicMin()) { - maxHeight = intrinsicHeight(); + maxHeight = intrinsicHeight(context.getContext()); } boolean hasHorizontalWrap = mWidthModifier.isWrap(); @@ -180,7 +181,8 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl if (isInHorizontalFill()) { measuredWidth = maxWidth; } else if (mWidthModifier.hasWeight()) { - measuredWidth = Math.max(measuredWidth, computeModifierDefinedWidth()); + measuredWidth = + Math.max(measuredWidth, computeModifierDefinedWidth(context.getContext())); } else { measuredWidth = Math.max(measuredWidth, minWidth); measuredWidth = Math.min(measuredWidth, maxWidth); @@ -188,7 +190,8 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl if (isInVerticalFill()) { // todo: potential npe -- bbade@ measuredHeight = maxHeight; } else if (mHeightModifier.hasWeight()) { - measuredHeight = Math.max(measuredHeight, computeModifierDefinedHeight()); + measuredHeight = + Math.max(measuredHeight, computeModifierDefinedHeight(context.getContext())); } else { measuredHeight = Math.max(measuredHeight, minHeight); measuredHeight = Math.min(measuredHeight, maxHeight); @@ -224,7 +227,9 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl computeSize(context, 0f, measuredWidth, 0, h, measure); mComponentModifiers.setVerticalScrollDimension(measuredHeight, h); } else { - computeSize(context, 0f, measuredWidth, 0f, measuredHeight, measure); + float maxChildWidth = measuredWidth - mPaddingLeft - mPaddingRight; + float maxChildHeight = measuredHeight - mPaddingTop - mPaddingBottom; + computeSize(context, 0f, maxChildWidth, 0f, maxChildHeight, measure); } } @@ -258,7 +263,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl super.layout(context, measure); ComponentMeasure self = measure.get(this); - mComponentModifiers.layout(context, self.getW(), self.getH()); + mComponentModifiers.layout(context, this, self.getW(), self.getH()); for (Component c : mChildrenComponents) { c.layout(context, measure); } @@ -275,7 +280,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl super.layout(context, measure); ComponentMeasure self = measure.get(this); - mComponentModifiers.layout(context, self.getW(), self.getH()); + mComponentModifiers.layout(context, this, self.getW(), self.getH()); this.mNeedsMeasure = false; } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java index 0ec820b85964..37b9a688af8b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.operations.layout.Component; @@ -167,11 +168,11 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation } @Override - public float intrinsicWidth() { - float width = computeModifierDefinedWidth(); + public float intrinsicWidth(@Nullable RemoteContext context) { + float width = computeModifierDefinedWidth(context); float componentWidths = 0f; for (Component c : mChildrenComponents) { - componentWidths += c.intrinsicWidth(); + componentWidths += c.intrinsicWidth(context); } return Math.max(width, componentWidths); } @@ -344,6 +345,11 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation DebugLog.e(); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "RowLayout"; @@ -395,6 +401,11 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation spacedBy)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java index 8e7f538d0004..910205e8a7e2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java @@ -34,11 +34,13 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure. import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; +import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent; import java.util.List; /** Text component, referencing a text id */ -public class TextLayout extends LayoutManager implements ComponentStartOperation, VariableSupport { +public class TextLayout extends LayoutManager + implements ComponentStartOperation, VariableSupport, AccessibleComponent { private static final boolean DEBUG = false; private int mTextId = -1; @@ -57,6 +59,12 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation @Nullable private String mCachedString = ""; + @Nullable + @Override + public Integer getTextId() { + return mTextId; + } + @Override public void registerListening(@NonNull RemoteContext context) { if (mTextId != -1) { @@ -92,6 +100,13 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation } mTextW = -1; mTextH = -1; + + if (mHorizontalScrollDelegate != null) { + mHorizontalScrollDelegate.reset(); + } + if (mVerticalScrollDelegate != null) { + mVerticalScrollDelegate.reset(); + } invalidateMeasure(); } @@ -175,6 +190,11 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation int length = mCachedString.length(); if (mTextW > mWidth) { context.save(); + context.clipRect( + mPaddingLeft, + mPaddingTop, + mWidth - mPaddingLeft - mPaddingRight, + mHeight - mPaddingTop - mPaddingBottom); context.translate(getScrollX(), getScrollY()); context.drawTextRun(mTextId, 0, length, 0, 0, mTextX, mTextY, false); context.restore(); @@ -285,15 +305,20 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation } @Override - public float intrinsicHeight() { + public float intrinsicHeight(@Nullable RemoteContext context) { return mTextH; } @Override - public float intrinsicWidth() { + public float intrinsicWidth(@Nullable RemoteContext context) { return mTextW; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "TextLayout"; @@ -361,6 +386,11 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation textAlign)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", id(), name()) .description("Text layout implementation.\n\n") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java index 5df16c5bc03a..b4240d0e08a7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java @@ -25,6 +25,7 @@ import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; @@ -98,7 +99,8 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation { } @Override - public void layout(@NonNull RemoteContext context, float width, float height) { + public void layout( + @NonNull RemoteContext context, Component component, float width, float height) { this.mWidth = width; this.mHeight = height; } @@ -109,6 +111,11 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation { return "BackgroundModifierOperation(" + mWidth + " x " + mHeight + ")"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -182,6 +189,11 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation { context.restorePaint(); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Modifier Operations", OP_CODE, CLASS_NAME) .description("define the Background Modifier") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java index bfadd2f1ef9c..df30d9f615e5 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java @@ -25,6 +25,7 @@ import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; @@ -124,7 +125,8 @@ public class BorderModifierOperation extends DecoratorModifierOperation { } @Override - public void layout(@NonNull RemoteContext context, float width, float height) { + public void layout( + @NonNull RemoteContext context, Component component, float width, float height) { this.mWidth = width; this.mHeight = height; } @@ -155,6 +157,11 @@ public class BorderModifierOperation extends DecoratorModifierOperation { + ")"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -240,6 +247,11 @@ public class BorderModifierOperation extends DecoratorModifierOperation { context.restorePaint(); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Modifier Operations", OP_CODE, CLASS_NAME) .description("define the Border Modifier") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java index d0af872acc53..b27fb9200398 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java @@ -23,6 +23,7 @@ import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; import java.util.List; @@ -40,7 +41,8 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation { } @Override - public void layout(@NonNull RemoteContext context, float width, float height) { + public void layout( + @NonNull RemoteContext context, Component component, float width, float height) { this.mWidth = width; this.mHeight = height; } @@ -55,6 +57,11 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation { apply(buffer); } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -83,6 +90,11 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation { operations.add(new ClipRectModifierOperation()); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Canvas Operations", OP_CODE, CLASS_NAME) .description("Draw the specified round-rect"); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java index d11f26f83ebd..d2ba13f69a91 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java @@ -21,6 +21,7 @@ import com.android.internal.widget.remotecompose.core.CoreDocument; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.PaintOperation; import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; import com.android.internal.widget.remotecompose.core.operations.MatrixSave; @@ -86,6 +87,10 @@ public class ComponentModifiers extends PaintOperation float tx = 0f; float ty = 0f; for (ModifierOperation op : mList) { + if (op.isDirty() && op instanceof VariableSupport) { + ((VariableSupport) op).updateVariables(context.getContext()); + op.markNotDirty(); + } if (op instanceof PaddingModifierOperation) { PaddingModifierOperation pop = (PaddingModifierOperation) op; context.translate(pop.getLeft(), pop.getTop()); @@ -109,7 +114,8 @@ public class ComponentModifiers extends PaintOperation } @Override - public void layout(@NonNull RemoteContext context, float width, float height) { + public void layout( + @NonNull RemoteContext context, Component component, float width, float height) { float w = width; float h = height; for (ModifierOperation op : mList) { @@ -119,9 +125,9 @@ public class ComponentModifiers extends PaintOperation h -= pop.getTop() + pop.getBottom(); } if (op instanceof ClickModifierOperation) { - ((DecoratorComponent) op).layout(context, width, height); + ((DecoratorComponent) op).layout(context, component, width, height); } else if (op instanceof DecoratorComponent) { - ((DecoratorComponent) op).layout(context, w, h); + ((DecoratorComponent) op).layout(context, component, w, h); } } } @@ -156,10 +162,16 @@ public class ComponentModifiers extends PaintOperation @Override public void onTouchUp( - RemoteContext context, CoreDocument document, Component component, float x, float y) { + RemoteContext context, + CoreDocument document, + Component component, + float x, + float y, + float dx, + float dy) { for (ModifierOperation op : mList) { if (op instanceof TouchHandler) { - ((TouchHandler) op).onTouchUp(context, document, component, x, y); + ((TouchHandler) op).onTouchUp(context, document, component, x, y, dx, dy); } } } @@ -208,32 +220,6 @@ public class ComponentModifiers extends PaintOperation return false; } - public float getScrollX() { - float scroll = 0; - for (ModifierOperation op : mList) { - if (op instanceof ScrollModifierOperation) { - ScrollModifierOperation scrollModifier = (ScrollModifierOperation) op; - if (scrollModifier.isHorizontalScroll()) { - scroll = Math.min(scroll, scrollModifier.getScrollX()); - } - } - } - return scroll; - } - - public float getScrollY() { - float scroll = 0; - for (ModifierOperation op : mList) { - if (op instanceof ScrollModifierOperation) { - ScrollModifierOperation scrollModifier = (ScrollModifierOperation) op; - if (scrollModifier.isVerticalScroll()) { - scroll = Math.min(scroll, scrollModifier.getScrollY()); - } - } - } - return scroll; - } - public void setHorizontalScrollDimension(float hostDimension, float contentDimension) { for (ModifierOperation op : mList) { if (op instanceof ScrollModifierOperation) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java index 1e6ccfcb5d34..c377b756ff38 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java @@ -90,6 +90,11 @@ public class ComponentVisibilityOperation extends Operation operations.add(new ComponentVisibilityOperation(valueId)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", OP_CODE, "ComponentVisibility") .description( @@ -125,5 +130,6 @@ public class ComponentVisibilityOperation extends Operation } @Override - public void layout(@NonNull RemoteContext context, float width, float height) {} + public void layout( + @NonNull RemoteContext context, Component component, float width, float height) {} } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java index 4252309b7e4c..15c2f46093d2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java @@ -27,6 +27,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.operations.layout.AnimatableValue; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; import java.util.List; @@ -202,6 +203,12 @@ public class GraphicsLayerModifierOperation extends DecoratorModifierOperation { return "GraphicsLayerModifierOperation(" + mScaleX + ", " + mScaleY + ")"; } + /** + * The name of the class + * + * @return the name + */ + @NonNull public static String name() { return CLASS_NAME; } @@ -306,5 +313,5 @@ public class GraphicsLayerModifierOperation extends DecoratorModifierOperation { } @Override - public void layout(RemoteContext context, float width, float height) {} + public void layout(RemoteContext context, Component component, float width, float height) {} } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java index 692b5269954a..ec078a9e73ea 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java @@ -32,6 +32,11 @@ public class HeightModifierOperation extends DimensionModifierOperation { private static final int OP_CODE = Operations.MODIFIER_HEIGHT; public static final String CLASS_NAME = "HeightModifierOperation"; + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -94,6 +99,11 @@ public class HeightModifierOperation extends DimensionModifierOperation { return "HEIGHT"; } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Modifier Operations", OP_CODE, CLASS_NAME) .description("define the animation") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java index 333e281d4abb..2e9d6619d011 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java @@ -99,6 +99,11 @@ public class HostActionOperation extends Operation implements ActionOperation { operations.add(new HostActionOperation(actionId)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", OP_CODE, "HostAction") .description("Host action. This operation represents a host action") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java index f9a4270905a1..49ef58e0fe53 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java @@ -125,6 +125,11 @@ public class HostNamedActionOperation extends Operation implements ActionOperati operations.add(new HostNamedActionOperation(textId, type, valueId)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", OP_CODE, "HostNamedAction") .description("Host Named action. This operation represents a host action") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java new file mode 100644 index 000000000000..8b6d49747976 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.layout.modifiers; + +import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT; + +import android.annotation.NonNull; + +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +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.operations.layout.ScrollDelegate; +import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; + +import java.util.List; + +/** Represents a Marquee modifier. */ +public class MarqueeModifierOperation extends DecoratorModifierOperation implements ScrollDelegate { + private static final int OP_CODE = Operations.MODIFIER_MARQUEE; + public static final String CLASS_NAME = "MarqueeModifierOperation"; + + int mIterations; + int mAnimationMode; + float mRepeatDelayMillis; + float mInitialDelayMillis; + float mSpacing; + float mVelocity; + + private float mComponentWidth; + private float mComponentHeight; + private float mContentWidth; + private float mContentHeight; + + public MarqueeModifierOperation( + int iterations, + int animationMode, + float repeatDelayMillis, + float initialDelayMillis, + float spacing, + float velocity) { + this.mIterations = iterations; + this.mAnimationMode = animationMode; + this.mRepeatDelayMillis = repeatDelayMillis; + this.mInitialDelayMillis = initialDelayMillis; + this.mSpacing = spacing; + this.mVelocity = velocity; + } + + public void setContentWidth(float value) { + mContentWidth = value; + } + + public void setContentHeight(float value) { + mContentHeight = value; + } + + @Override + public float getScrollX(float currentValue) { + return mScrollX; + } + + @Override + public float getScrollY(float currentValue) { + return 0; + } + + @Override + public boolean handlesHorizontalScroll() { + return true; + } + + @Override + public boolean handlesVerticalScroll() { + return false; + } + + /** + * Reset the modifier + */ + public void reset() { + mLastTime = 0; + mScrollX = 0f; + } + + @Override + public void write(WireBuffer buffer) { + apply( + buffer, + mIterations, + mAnimationMode, + mRepeatDelayMillis, + mInitialDelayMillis, + mSpacing, + mVelocity); + } + + // @Override + public void serializeToString(int indent, StringSerializer serializer) { + serializer.append(indent, "MARQUEE = [" + mIterations + "]"); + } + + @NonNull + @Override + public String deepToString(@NonNull String indent) { + return (indent != null ? indent : "") + toString(); + } + + private long mLastTime = 0; + private long mStartTime = 0; + + private float mScrollX = 0f; + + @Override + public void paint(PaintContext context) { + long currentTime = System.currentTimeMillis(); + if (mLastTime == 0) { + mLastTime = currentTime; + mStartTime = mLastTime + (long) mInitialDelayMillis; + context.needsRepaint(); + } + if (mContentWidth > mComponentWidth && currentTime - mStartTime > mInitialDelayMillis) { + float density = context.getContext().getDensity(); // in dp + float delta = mContentWidth - mComponentWidth; + float duration = delta / (density * mVelocity); + float elapsed = ((System.currentTimeMillis() - mStartTime) / 1000f); + elapsed = (elapsed % duration) / duration; + float offset = + (1f + (float) Math.sin(elapsed * 2 * Math.PI - Math.PI / 2f)) / 2f * -delta; + + mScrollX = offset; + context.needsRepaint(); + } + } + + @Override + public String toString() { + return "MarqueeModifierOperation(" + mIterations + ")"; + } + + public static String name() { + return CLASS_NAME; + } + + public static int id() { + return OP_CODE; + } + + public static void apply( + WireBuffer buffer, + int iterations, + int animationMode, + float repeatDelayMillis, + float initialDelayMillis, + float spacing, + float velocity) { + buffer.start(OP_CODE); + buffer.writeInt(iterations); + buffer.writeInt(animationMode); + buffer.writeFloat(repeatDelayMillis); + buffer.writeFloat(initialDelayMillis); + buffer.writeFloat(spacing); + buffer.writeFloat(velocity); + } + + public static void read(WireBuffer buffer, List<Operation> operations) { + int iterations = buffer.readInt(); + int animationMode = buffer.readInt(); + float repeatDelayMillis = buffer.readFloat(); + float initialDelayMillis = buffer.readFloat(); + float spacing = buffer.readFloat(); + float velocity = buffer.readFloat(); + operations.add( + new MarqueeModifierOperation( + iterations, + animationMode, + repeatDelayMillis, + initialDelayMillis, + spacing, + velocity)); + } + + public static void documentation(DocumentationBuilder doc) { + doc.operation("Modifier Operations", OP_CODE, CLASS_NAME) + .description("specify a Marquee Modifier") + .field(FLOAT, "value", ""); + } + + @Override + public void layout(RemoteContext context, Component component, float width, float height) { + mComponentWidth = width; + mComponentHeight = height; + if (component instanceof LayoutComponent) { + LayoutComponent layoutComponent = (LayoutComponent) component; + setContentWidth(layoutComponent.intrinsicWidth(context)); + setContentHeight(layoutComponent.intrinsicHeight(context)); + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java index 69c4e9a8e423..42719478faf0 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java @@ -26,6 +26,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.operations.Utils; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; import java.util.List; @@ -90,6 +91,12 @@ public class OffsetModifierOperation extends DecoratorModifierOperation { return "OffsetModifierOperation(" + mX + ", " + mY + ")"; } + /** + * The name of the class + * + * @return the name + */ + @NonNull public static String name() { return CLASS_NAME; } @@ -123,5 +130,5 @@ public class OffsetModifierOperation extends DecoratorModifierOperation { } @Override - public void layout(RemoteContext context, float width, float height) {} + public void layout(RemoteContext context, Component component, float width, float height) {} } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java index 545df64ab154..bcfbdd68472f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java @@ -113,6 +113,11 @@ public class PaddingModifierOperation extends Operation implements ModifierOpera + ")"; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -150,6 +155,11 @@ public class PaddingModifierOperation extends Operation implements ModifierOpera operations.add(new PaddingModifierOperation(left, top, right, bottom)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Modifier Operations", OP_CODE, CLASS_NAME) .description("define the Padding Modifier") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java new file mode 100644 index 000000000000..fe074e4754e2 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.layout.modifiers; + +import android.annotation.NonNull; + +import com.android.internal.widget.remotecompose.core.CoreDocument; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.operations.Utils; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; +import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent; +import com.android.internal.widget.remotecompose.core.operations.layout.TouchHandler; +import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; +import com.android.internal.widget.remotecompose.core.operations.utilities.ColorUtils; +import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; +import com.android.internal.widget.remotecompose.core.operations.utilities.easing.Easing; +import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; + +import java.util.List; + +/** Represents a ripple effect */ +public class RippleModifierOperation extends DecoratorModifierOperation implements TouchHandler { + private static final int OP_CODE = Operations.MODIFIER_RIPPLE; + + long mAnimateRippleStart = 0; + float mAnimateRippleX = 0f; + float mAnimateRippleY = 0f; + int mAnimateRippleDuration = 1000; + + float mWidth = 0; + float mHeight = 0; + + @NonNull public float[] locationInWindow = new float[2]; + + @NonNull PaintBundle mPaint = new PaintBundle(); + + /** + * Animate the ripple effect + * + * @param x + * @param y + */ + public void animateRipple(float x, float y) { + mAnimateRippleStart = System.currentTimeMillis(); + mAnimateRippleX = x; + mAnimateRippleY = y; + } + + @Override + public void write(@NonNull WireBuffer buffer) { + apply(buffer); + } + + @NonNull + @Override + public String toString() { + return "RippleModifier"; + } + + @Override + public void apply(@NonNull RemoteContext context) { + RootLayoutComponent root = context.getDocument().getRootLayoutComponent(); + if (root != null) { + root.setHasTouchListeners(true); + } + } + + @NonNull + @Override + public String deepToString(@NonNull String indent) { + return (indent != null ? indent : "") + toString(); + } + + @Override + public void paint(@NonNull PaintContext context) { + if (mAnimateRippleStart == 0) { + return; + } + context.needsRepaint(); + + float progress = (System.currentTimeMillis() - mAnimateRippleStart); + progress /= (float) mAnimateRippleDuration; + if (progress > 1f) { + mAnimateRippleStart = 0; + } + progress = Math.min(1f, progress); + context.save(); + context.savePaint(); + mPaint.reset(); + + FloatAnimation anim1 = + new FloatAnimation(Easing.CUBIC_STANDARD, 1f, null, Float.NaN, Float.NaN); + anim1.setInitialValue(0f); + anim1.setTargetValue(1f); + float tween = anim1.get(progress); + + FloatAnimation anim2 = + new FloatAnimation(Easing.CUBIC_STANDARD, 0.5f, null, Float.NaN, Float.NaN); + anim2.setInitialValue(0f); + anim2.setTargetValue(1f); + float tweenRadius = anim2.get(progress); + + int startColor = ColorUtils.createColor(250, 250, 250, 180); + int endColor = ColorUtils.createColor(200, 200, 200, 0); + int paintedColor = Utils.interpolateColor(startColor, endColor, tween); + + float radius = Math.max(mWidth, mHeight) * tweenRadius; + mPaint.setColor(paintedColor); + context.applyPaint(mPaint); + context.clipRect(0f, 0f, mWidth, mHeight); + context.drawCircle(mAnimateRippleX, mAnimateRippleY, radius); + context.restorePaint(); + context.restore(); + } + + @Override + public void layout( + @NonNull RemoteContext context, Component component, float width, float height) { + mWidth = width; + mHeight = height; + } + + @Override + public void serializeToString(int indent, @NonNull StringSerializer serializer) { + serializer.append(indent, "RIPPLE_MODIFIER"); + } + + @NonNull + public static String name() { + return "RippleModifier"; + } + + public static void apply(@NonNull WireBuffer buffer) { + buffer.start(OP_CODE); + } + + public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { + operations.add(new RippleModifierOperation()); + } + + public static void documentation(@NonNull DocumentationBuilder doc) { + doc.operation("Layout Operations", OP_CODE, name()) + .description( + "Ripple modifier. This modifier will do a ripple animation on touch down"); + } + + @Override + public void onTouchDown( + RemoteContext context, CoreDocument document, Component component, float x, float y) { + locationInWindow[0] = 0f; + locationInWindow[1] = 0f; + component.getLocationInWindow(locationInWindow); + animateRipple(x - locationInWindow[0], y - locationInWindow[1]); + context.hapticEffect(3); + } + + @Override + public void onTouchUp( + RemoteContext context, + CoreDocument document, + Component component, + float x, + float y, + float dx, + float dy) {} + + @Override + public void onTouchDrag( + RemoteContext context, CoreDocument document, Component component, float x, float y) {} + + @Override + public void onTouchCancel( + RemoteContext context, CoreDocument document, Component component, float x, float y) {} +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java index 681501d9cdf9..4c1f04ebd3d4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java @@ -26,6 +26,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.operations.DrawBase4; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; @@ -57,6 +58,11 @@ public class RoundedClipRectModifierOperation extends DrawBase4 return OP_CODE; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -67,6 +73,11 @@ public class RoundedClipRectModifierOperation extends DrawBase4 apply(buffer, v1, v2, v3, v4); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Modifier Operations", id(), "RoundedClipRectModifierOperation") .description("clip with rectangle") @@ -107,7 +118,8 @@ public class RoundedClipRectModifierOperation extends DrawBase4 } @Override - public void layout(@NonNull RemoteContext context, float width, float height) { + public void layout( + @NonNull RemoteContext context, Component component, float width, float height) { this.mWidth = width; this.mHeight = height; } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java index 0b6632057bd2..a5f79ee7e7b7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java @@ -24,18 +24,24 @@ import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.operations.TouchExpression; import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.layout.Component; +import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent; +import com.android.internal.widget.remotecompose.core.operations.layout.ListActionsOperation; import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent; +import com.android.internal.widget.remotecompose.core.operations.layout.ScrollDelegate; import com.android.internal.widget.remotecompose.core.operations.layout.TouchHandler; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; import java.util.List; /** Represents a scroll modifier. */ -public class ScrollModifierOperation extends DecoratorModifierOperation implements TouchHandler { +public class ScrollModifierOperation extends ListActionsOperation + implements TouchHandler, DecoratorComponent, ScrollDelegate, VariableSupport { private static final int OP_CODE = Operations.MODIFIER_SCROLL; public static final String CLASS_NAME = "ScrollModifierOperation"; @@ -43,9 +49,6 @@ public class ScrollModifierOperation extends DecoratorModifierOperation implemen private final float mMax; private final float mNotchMax; - float mWidth = 0; - float mHeight = 0; - int mDirection; float mTouchDownX; @@ -63,13 +66,44 @@ public class ScrollModifierOperation extends DecoratorModifierOperation implemen float mHostDimension; float mContentDimension; + private TouchExpression mTouchExpression; + public ScrollModifierOperation(int direction, float position, float max, float notchMax) { + super("SCROLL_MODIFIER"); this.mDirection = direction; this.mPositionExpression = position; this.mMax = max; this.mNotchMax = notchMax; } + /** + * Inflate the operation + * + * @param component + */ + public void inflate(Component component) { + for (Operation op : mList) { + if (op instanceof TouchExpression) { + mTouchExpression = (TouchExpression) op; + mTouchExpression.setComponent(component); + } + } + } + + @Override + public void registerListening(@NonNull RemoteContext context) { + if (mTouchExpression != null) { + mTouchExpression.registerListening(context); + } + } + + @Override + public void updateVariables(@NonNull RemoteContext context) { + if (mTouchExpression != null) { + mTouchExpression.updateVariables(context); + } + } + public boolean isVerticalScroll() { return mDirection == 0; } @@ -113,6 +147,12 @@ public class ScrollModifierOperation extends DecoratorModifierOperation implemen @Override public void paint(PaintContext context) { + for (Operation op : mList) { + op.apply(context.getContext()); + } + if (mTouchExpression == null) { + return; + } float position = context.getContext() .mRemoteComposeState @@ -130,6 +170,12 @@ public class ScrollModifierOperation extends DecoratorModifierOperation implemen return "ScrollModifierOperation(" + mDirection + ")"; } + /** + * The name of the class + * + * @return the name + */ + @NonNull public static String name() { return CLASS_NAME; } @@ -167,7 +213,7 @@ public class ScrollModifierOperation extends DecoratorModifierOperation implemen } @Override - public void layout(RemoteContext context, float width, float height) { + public void layout(RemoteContext context, Component component, float width, float height) { mWidth = width; mHeight = height; if (mDirection == 0) { // VERTICAL @@ -186,18 +232,36 @@ public class ScrollModifierOperation extends DecoratorModifierOperation implemen mTouchDownY = y; mInitialScrollX = mScrollX; mInitialScrollY = mScrollY; + if (mTouchExpression != null) { + mTouchExpression.updateVariables(context); + mTouchExpression.touchDown(context, x + mScrollX, y + mScrollY); + } document.appliedTouchOperation(component); } @Override public void onTouchUp( - RemoteContext context, CoreDocument document, Component component, float x, float y) { + RemoteContext context, + CoreDocument document, + Component component, + float x, + float y, + float dx, + float dy) { + if (mTouchExpression != null) { + mTouchExpression.updateVariables(context); + mTouchExpression.touchUp(context, x + mScrollX, y + mScrollY, dx, dy); + } // If not using touch expression, should add velocity decay here } @Override public void onTouchDrag( RemoteContext context, CoreDocument document, Component component, float x, float y) { + if (mTouchExpression != null) { + mTouchExpression.updateVariables(context); + mTouchExpression.touchDrag(context, x + mScrollX, y + mScrollY); + } float dx = x - mTouchDownX; float dy = y - mTouchDownY; @@ -229,4 +293,35 @@ public class ScrollModifierOperation extends DecoratorModifierOperation implemen public float getContentDimension() { return mContentDimension; } + + @Override + public float getScrollX(float currentValue) { + if (mDirection == 1) { + return mScrollX; + } + return 0f; + } + + @Override + public float getScrollY(float currentValue) { + if (mDirection == 0) { + return mScrollY; + } + return 0f; + } + + @Override + public boolean handlesHorizontalScroll() { + return mDirection == 1; + } + + @Override + public boolean handlesVerticalScroll() { + return mDirection == 0; + } + + @Override + public void reset() { + // nothing here for now + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java index b96d3cc4bbc0..b6977a035c9e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java @@ -73,7 +73,6 @@ public class ValueFloatChangeActionOperation extends Operation implements Action @Override public void runAction( RemoteContext context, CoreDocument document, Component component, float x, float y) { - System.out.println("OVERRIDE " + mTargetValueId + " TO " + mValue); context.overrideFloat(mTargetValueId, mValue); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java index d81b7ffd1ef9..766271a70ce4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java @@ -101,6 +101,11 @@ public class ValueFloatExpressionChangeActionOperation extends Operation operations.add(new ValueFloatExpressionChangeActionOperation(valueId, value)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", OP_CODE, "ValueIntegerExpressionChangeActionOperation") .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java index fb13b42dbd21..60166a7b2102 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java @@ -99,6 +99,11 @@ public class ValueIntegerChangeActionOperation extends Operation implements Acti operations.add(new ValueIntegerChangeActionOperation(valueId, value)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", OP_CODE, "ValueIntegerChangeActionOperation") .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java index 0fe88ad165a9..502508058465 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java @@ -101,6 +101,11 @@ public class ValueIntegerExpressionChangeActionOperation extends Operation operations.add(new ValueIntegerExpressionChangeActionOperation(valueId, value)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", OP_CODE, "ValueIntegerExpressionChangeActionOperation") .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java index a8d3b87f04b4..8093bb3c64ec 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java @@ -103,6 +103,11 @@ public class ValueStringChangeActionOperation extends Operation implements Actio operations.add(new ValueStringChangeActionOperation(valueId, value)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Layout Operations", OP_CODE, "ValueStringChangeActionOperation") .description( diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java index f6d743f599d3..05305988a49f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java @@ -32,6 +32,11 @@ public class WidthModifierOperation extends DimensionModifierOperation { private static final int OP_CODE = Operations.MODIFIER_WIDTH; public static final String CLASS_NAME = "WidthModifierOperation"; + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return CLASS_NAME; @@ -94,6 +99,11 @@ public class WidthModifierOperation extends DimensionModifierOperation { return "WIDTH"; } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Modifier Operations", OP_CODE, CLASS_NAME) .description("define the animation") diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java index 96ed2cda3e10..35de33a9997a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java @@ -26,6 +26,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.operations.Utils; +import com.android.internal.widget.remotecompose.core.operations.layout.Component; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; import java.util.List; @@ -45,7 +46,7 @@ public class ZIndexModifierOperation extends DecoratorModifierOperation { return mCurrentValue; } - public void setmValue(float value) { + public void setValue(float value) { this.mValue = value; } @@ -79,6 +80,12 @@ public class ZIndexModifierOperation extends DecoratorModifierOperation { return "ZIndexModifierOperation(" + mValue + ")"; } + /** + * The name of the class + * + * @return the name + */ + @NonNull public static String name() { return CLASS_NAME; } @@ -109,5 +116,5 @@ public class ZIndexModifierOperation extends DecoratorModifierOperation { } @Override - public void layout(RemoteContext context, float width, float height) {} + public void layout(RemoteContext context, Component component, float width, float height) {} } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java index a56874781e4a..7e467012536d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java @@ -20,58 +20,147 @@ import android.annotation.Nullable; import com.android.internal.widget.remotecompose.core.operations.utilities.easing.MonotonicSpline; +import java.util.Random; + /** high performance floating point expression evaluator used in animation */ public class AnimatedFloatExpression { @NonNull static IntMap<String> sNames = new IntMap<>(); + + /** The START POINT in the float NaN space for operators */ public static final int OFFSET = 0x310_000; + + /** ADD operator */ public static final float ADD = asNan(OFFSET + 1); + + /** SUB operator */ public static final float SUB = asNan(OFFSET + 2); + + /** MUL operator */ public static final float MUL = asNan(OFFSET + 3); + + /** DIV operator */ public static final float DIV = asNan(OFFSET + 4); + + /** MOD operator */ public static final float MOD = asNan(OFFSET + 5); + + /** MIN operator */ public static final float MIN = asNan(OFFSET + 6); + + /** MAX operator */ public static final float MAX = asNan(OFFSET + 7); + + /** POW operator */ public static final float POW = asNan(OFFSET + 8); + + /** SQRT operator */ public static final float SQRT = asNan(OFFSET + 9); + + /** ABS operator */ public static final float ABS = asNan(OFFSET + 10); + + /** SIGN operator */ public static final float SIGN = asNan(OFFSET + 11); + + /** COPY_SIGN operator */ public static final float COPY_SIGN = asNan(OFFSET + 12); + + /** EXP operator */ public static final float EXP = asNan(OFFSET + 13); + + /** FLOOR operator */ public static final float FLOOR = asNan(OFFSET + 14); + + /** LOG operator */ public static final float LOG = asNan(OFFSET + 15); + + /** LN operator */ public static final float LN = asNan(OFFSET + 16); + + /** ROUND operator */ public static final float ROUND = asNan(OFFSET + 17); + + /** SIN operator */ public static final float SIN = asNan(OFFSET + 18); + + /** COS operator */ public static final float COS = asNan(OFFSET + 19); + + /** TAN operator */ public static final float TAN = asNan(OFFSET + 20); + + /** ASIN operator */ public static final float ASIN = asNan(OFFSET + 21); + + /** ACOS operator */ public static final float ACOS = asNan(OFFSET + 22); + /** ATAN operator */ public static final float ATAN = asNan(OFFSET + 23); + /** ATAN2 operator */ public static final float ATAN2 = asNan(OFFSET + 24); + + /** MAD operator */ public static final float MAD = asNan(OFFSET + 25); + + /** IFELSE operator */ public static final float IFELSE = asNan(OFFSET + 26); + /** CLAMP operator */ public static final float CLAMP = asNan(OFFSET + 27); + + /** CBRT operator */ public static final float CBRT = asNan(OFFSET + 28); + + /** DEG operator */ public static final float DEG = asNan(OFFSET + 29); + + /** RAD operator */ public static final float RAD = asNan(OFFSET + 30); + + /** CEIL operator */ public static final float CEIL = asNan(OFFSET + 31); // Array ops + /** A DEREF operator */ public static final float A_DEREF = asNan(OFFSET + 32); + + /** Array MAX operator */ public static final float A_MAX = asNan(OFFSET + 33); + + /** Array MIN operator */ public static final float A_MIN = asNan(OFFSET + 34); + + /** A_SUM operator */ public static final float A_SUM = asNan(OFFSET + 35); + + /** A_AVG operator */ public static final float A_AVG = asNan(OFFSET + 36); + + /** A_LEN operator */ public static final float A_LEN = asNan(OFFSET + 37); + + /** A_SPLINE operator */ public static final float A_SPLINE = asNan(OFFSET + 38); - public static final int LAST_OP = OFFSET + 38; + /** RAND Random number 0..1 */ + public static final float RAND = asNan(OFFSET + 39); + + /** RAND_SEED operator */ + public static final float RAND_SEED = asNan(OFFSET + 40); + + /** LAST valid operator */ + public static final int LAST_OP = OFFSET + 40; - public static final float VAR1 = asNan(OFFSET + 39); - public static final float VAR2 = asNan(OFFSET + 40); + /** VAR1 operator */ + public static final float VAR1 = asNan(OFFSET + 41); + + /** VAR2 operator */ + public static final float VAR2 = asNan(OFFSET + 42); + + /** VAR2 operator */ + public static final float VAR3 = asNan(OFFSET + 43); // TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR // private static final float FP_PI = (float) Math.PI; @@ -83,6 +172,7 @@ public class AnimatedFloatExpression { @NonNull float[] mVar = new float[0]; @Nullable CollectionsAccess mCollectionsAccess; IntMap<MonotonicSpline> mSplineMap = new IntMap<>(); + private Random mRandom; private float getSplineValue(int arrayId, float pos) { MonotonicSpline fit = mSplineMap.get(arrayId); @@ -151,10 +241,11 @@ public class AnimatedFloatExpression { /** * Evaluate a float expression * - * @param ca - * @param exp - * @param var - * @return + * @param ca Access to float array collections + * @param exp the expressions + * @param len the length of the expression array + * @param var variables if the expression contains VAR tags + * @return the value the expression evaluated to */ public float eval( @NonNull CollectionsAccess ca, @NonNull float[] exp, int len, @NonNull float... var) { @@ -183,9 +274,10 @@ public class AnimatedFloatExpression { /** * Evaluate a float expression * - * @param ca - * @param exp - * @return + * @param ca The access to float arrays + * @param exp the expression + * @param len the length of the expression sections + * @return the value the expression evaluated to */ public float eval(@NonNull CollectionsAccess ca, @NonNull float[] exp, int len) { System.arraycopy(exp, 0, mLocalStack, 0, len); @@ -304,6 +396,8 @@ public class AnimatedFloatExpression { sNames.put(k++, "A_AVG"); sNames.put(k++, "A_LEN"); sNames.put(k++, "A_SPLINE"); + sNames.put(k++, "RAND"); + sNames.put(k++, "RAND_SEED"); sNames.put(k++, "a[0]"); sNames.put(k++, "a[1]"); @@ -518,9 +612,12 @@ public class AnimatedFloatExpression { private static final int OP_A_AVG = OFFSET + 36; private static final int OP_A_LEN = OFFSET + 37; private static final int OP_A_SPLINE = OFFSET + 38; - private static final int OP_FIRST_VAR = OFFSET + 39; - private static final int OP_SECOND_VAR = OFFSET + 40; - private static final int OP_THIRD_VAR = OFFSET + 41; + private static final int OP_RAND = OFFSET + 39; + private static final int OP_RAND_SEED = OFFSET + 40; + + private static final int OP_FIRST_VAR = OFFSET + 41; + private static final int OP_SECOND_VAR = OFFSET + 42; + private static final int OP_THIRD_VAR = OFFSET + 43; int opEval(int sp, int id) { float[] array; @@ -708,6 +805,26 @@ public class AnimatedFloatExpression { mStack[sp - 1] = getSplineValue(id, mStack[sp]); return sp - 1; + case OP_RAND: + if (mRandom == null) { + mRandom = new Random(); + } + mStack[sp + 1] = mRandom.nextFloat(); + return sp + 1; + + case OP_RAND_SEED: + float seed = mStack[sp]; + if (seed == 0) { + mRandom = new Random(); + } else { + if (mRandom == null) { + mRandom = new Random(Float.floatToRawIntBits(seed)); + } else { + mRandom.setSeed(Float.floatToRawIntBits(seed)); + } + } + return sp - 1; + case OP_FIRST_VAR: mStack[sp] = mVar[0]; return sp; diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java index 182d36a5eb06..69de5354a923 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java @@ -23,17 +23,45 @@ import android.annotation.Nullable; * FloatArrayAccess, ListAccess, MapAccess */ public interface ArrayAccess { + /** + * Get a value as a float for an index + * + * @param index position in the collection + * @return + */ float getFloatValue(int index); + /** + * If the objects have id's return the id + * + * @param index index of the object + * @return id or -1 if no id is available + */ default int getId(int index) { return 0; } + /** + * Get the backing array of float if available for float arrays + * + * @return + */ @Nullable float[] getFloats(); + /** + * Get the length of the collection + * + * @return length of the collection + */ int getLength(); + /** + * Get the value as an integer if available + * + * @param index the position in the collection + * @return it value as and integer + */ default int getIntValue(int index) { return (int) getFloatValue(index); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilityModifier.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilityModifier.java new file mode 100644 index 000000000000..cd8b7b865cd2 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilityModifier.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.semantics; + +import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation; + +/** A Modifier that provides semantic info. */ +public interface AccessibilityModifier extends ModifierOperation, AccessibleComponent { + int getOpCode(); +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilitySemantics.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilitySemantics.java new file mode 100644 index 000000000000..291ad477703b --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilitySemantics.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.semantics; + +/** Marker interface for a Component or Modifier that is relevant for Semantics. */ +public interface AccessibilitySemantics { + + /** + * Determines if this element is interesting for semantic analysis. + * + * <p>This method is used to filter elements during semantic analysis. By default, all elements + * are considered interesting. Subclasses can override this method to exclude specific elements + * from semantic analysis. + * + * @return {@code true} if this element is interesting for semantic analysis, {@code false} + * otherwise. + */ + default boolean isInterestingForSemantics() { + return true; + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java new file mode 100644 index 000000000000..e07fc4d9a8f8 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.semantics; + +import android.annotation.Nullable; + +public interface AccessibleComponent extends AccessibilitySemantics { + default @Nullable Integer getContentDescriptionId() { + return null; + } + + default @Nullable Integer getTextId() { + return null; + } + + default @Nullable Role getRole() { + return null; + } + + default boolean isClickable() { + return false; + } + + default CoreSemantics.Mode getMode() { + return CoreSemantics.Mode.SET; + } + + // Our master list + // https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/Role + enum Role { + BUTTON("Button"), + CHECKBOX("Checkbox"), + SWITCH("Switch"), + RADIO_BUTTON("RadioButton"), + TAB("Tab"), + IMAGE("Image"), + DROPDOWN_LIST("DropdownList"), + PICKER("Picker"), + CAROUSEL("Carousel"), + UNKNOWN(null); + + @Nullable private final String mDescription; + + Role(@Nullable String description) { + this.mDescription = description; + } + + @Nullable + public String getDescription() { + return mDescription; + } + + public static Role fromInt(int i) { + if (i < UNKNOWN.ordinal()) { + return Role.values()[i]; + } + return Role.UNKNOWN; + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/CoreSemantics.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/CoreSemantics.java new file mode 100644 index 000000000000..b8166e668ac1 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/CoreSemantics.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.semantics; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; + +import java.util.List; + +/** Implementation of the most common semantics used in typical Android apps. */ +public class CoreSemantics extends Operation implements AccessibilityModifier { + public int mContentDescriptionId = 0; + public @Nullable Role mRole = null; + public int mTextId = 0; + public int mStateDescriptionId = 0; + public boolean mEnabled = true; + public Mode mMode = Mode.SET; + public boolean mClickable = false; + + @Override + public int getOpCode() { + return Operations.ACCESSIBILITY_SEMANTICS; + } + + @Nullable + @Override + public Role getRole() { + return mRole; + } + + @Override + public void write(WireBuffer buffer) { + buffer.writeInt(mContentDescriptionId); + buffer.writeByte((mRole != null) ? mRole.ordinal() : -1); + buffer.writeInt(mTextId); + buffer.writeInt(mStateDescriptionId); + buffer.writeByte(mMode.ordinal()); + buffer.writeBoolean(mEnabled); + buffer.writeBoolean(mClickable); + } + + private void read(WireBuffer buffer) { + mContentDescriptionId = buffer.readInt(); + mRole = Role.fromInt(buffer.readByte()); + mTextId = buffer.readInt(); + mStateDescriptionId = buffer.readInt(); + mMode = Mode.values()[buffer.readByte()]; + mEnabled = buffer.readBoolean(); + mClickable = buffer.readBoolean(); + } + + @Override + public void apply(RemoteContext context) { + // Handled via touch helper + } + + @NonNull + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("SEMANTICS"); + if (mRole != null) { + builder.append(" "); + builder.append(mRole); + } + if (mContentDescriptionId > 0) { + builder.append(" contentDescription="); + builder.append(mContentDescriptionId); + } + if (mTextId > 0) { + builder.append(" text="); + builder.append(mTextId); + } + if (mStateDescriptionId > 0) { + builder.append(" stateDescription="); + builder.append(mStateDescriptionId); + } + if (!mEnabled) { + builder.append(" disabled"); + } + if (mClickable) { + builder.append(" clickable"); + } + return builder.toString(); + } + + @Nullable + @Override + public String deepToString(String indent) { + return indent + this; + } + + @NonNull + public String serializedName() { + return "SEMANTICS"; + } + + @Override + public void serializeToString(int indent, @NonNull StringSerializer serializer) { + serializer.append(indent, serializedName() + " = " + this); + } + + public static void read(WireBuffer buffer, List<Operation> operations) { + CoreSemantics semantics = new CoreSemantics(); + + semantics.read(buffer); + + operations.add(semantics); + } + + @Override + public Integer getContentDescriptionId() { + return mContentDescriptionId != 0 ? mContentDescriptionId : null; + } + + public @Nullable Integer getStateDescriptionId() { + return mStateDescriptionId != 0 ? mStateDescriptionId : null; + } + + public @Nullable Integer getTextId() { + return mTextId != 0 ? mTextId : null; + } + + public enum Mode { + SET, + CLEAR_AND_SET, + MERGE + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java index 975213f76bd2..2c874b183a62 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java @@ -68,6 +68,11 @@ public class BooleanConstant extends Operation { return "BooleanConstant[" + mId + "] = " + mValue + ""; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "OrigamiBoolean"; @@ -108,6 +113,11 @@ public class BooleanConstant extends Operation { operations.add(new BooleanConstant(id, value)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, "BooleanConstant") .description("A boolean and its associated id") diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java index 210a15ac7ca4..5462d3e069ed 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java @@ -60,6 +60,11 @@ public class IntegerConstant extends Operation { return "IntegerConstant[" + mId + "] = " + mValue + ""; } + /** + * The name of the class + * + * @return the name + */ @NonNull public static String name() { return "IntegerConstant"; @@ -100,6 +105,11 @@ public class IntegerConstant extends Operation { operations.add(new IntegerConstant(id, value)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", id(), "IntegerConstant") .description("A integer and its associated id") diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java index 9875c935c112..1a3cdb1a96d7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java @@ -96,6 +96,11 @@ public class LongConstant extends Operation { operations.add(new LongConstant(id, value)); } + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ public static void documentation(@NonNull DocumentationBuilder doc) { doc.operation("Expressions Operations", OP_CODE, "LongConstant") .description("A boolean and its associated id") diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java index 19b4b36b504c..19453a0016a1 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java +++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java @@ -85,6 +85,7 @@ public class RemoteComposePlayer extends FrameLayout { } } else { mInner.setDocument(null); + this.setAccessibilityDelegate(null); } mapColors(); setupSensors(); diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java index bc7d5e108e1d..daa44c8d7312 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java @@ -250,7 +250,7 @@ public class AndroidPaintContext extends PaintContext { @Override public void getTextBounds(int textId, int start, int end, int flags, @NonNull float[] bounds) { String str = getText(textId); - if (end == -1) { + if (end == -1 || end > str.length()) { end = str.length(); } @@ -757,11 +757,17 @@ public class AndroidPaintContext extends PaintContext { private Path getPath(int id, float start, float end) { AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext; + Path p = (Path) androidContext.mRemoteComposeState.getPath(id); + if (p != null) { + return p; + } Path path = new Path(); float[] pathData = androidContext.mRemoteComposeState.getPathData(id); if (pathData != null) { FloatsToPath.genPath(path, pathData, start, end); + androidContext.mRemoteComposeState.putPath(id, path); } + return path; } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java index ecfd13aa66b6..8da5b9d161f2 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java @@ -241,9 +241,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta case MotionEvent.ACTION_DOWN: mActionDownPoint.x = (int) event.getX(); mActionDownPoint.y = (int) event.getY(); - mInActionDown = true; CoreDocument doc = mDocument.getDocument(); if (doc.hasTouchListener()) { + mInActionDown = true; if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } else { @@ -251,8 +251,10 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta } mVelocityTracker.addMovement(event); doc.touchDown(mARContext, event.getX(), event.getY()); + invalidate(); + return true; } - return true; + return false; case MotionEvent.ACTION_CANCEL: mInActionDown = false; @@ -262,8 +264,10 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta float dx = mVelocityTracker.getXVelocity(pointerId); float dy = mVelocityTracker.getYVelocity(pointerId); doc.touchCancel(mARContext, event.getX(), event.getY(), dx, dy); + invalidate(); + return true; } - return true; + case MotionEvent.ACTION_UP: mInActionDown = false; performClick(); @@ -273,8 +277,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta float dx = mVelocityTracker.getXVelocity(pointerId); float dy = mVelocityTracker.getYVelocity(pointerId); doc.touchUp(mARContext, event.getX(), event.getY(), dx, dy); + invalidate(); + return true; } - return true; case MotionEvent.ACTION_MOVE: if (mInActionDown) { @@ -286,6 +291,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta invalidate(); } } + return true; } } return false; |