summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java47
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java79
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java65
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java127
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java20
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java140
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java32
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java1
22 files changed, 581 insertions, 79 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
index a53d6b899898..3fe6873028cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
@@ -132,8 +132,13 @@ public class AndroidPlatformSemanticNodeApplier
}
}
- // TODO correct values
- nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+ if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
+ nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1, -1, false));
+ nodeInfo.setClassName("android.widget.HorizontalScrollView");
+ } else {
+ nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+ nodeInfo.setClassName("android.widget.ScrollView");
+ }
if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
nodeInfo.setClassName("android.widget.HorizontalScrollView");
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
index f70f4cbceb70..db2c46046561 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
@@ -33,6 +33,7 @@ import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySem
import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics;
import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent;
+import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent.ScrollDirection;
import java.util.ArrayList;
import java.util.Collections;
@@ -104,9 +105,9 @@ public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibi
if (isClickAction(action)) {
return performClick(component);
} else if (isScrollForwardAction(action)) {
- return scrollByOffset(mRemoteContext, component, -500) != 0;
+ return scrollDirection(mRemoteContext, component, ScrollDirection.FORWARD);
} else if (isScrollBackwardAction(action)) {
- return scrollByOffset(mRemoteContext, component, 500) != 0;
+ return scrollDirection(mRemoteContext, component, ScrollDirection.BACKWARD);
} else if (isShowOnScreenAction(action)) {
return showOnScreen(mRemoteContext, component);
} else {
@@ -141,17 +142,30 @@ public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibi
}
private boolean showOnScreen(RemoteContext context, Component component) {
- if (component.getParent() instanceof LayoutComponent) {
- LayoutComponent parent = (LayoutComponent) component.getParent();
+ ScrollableComponent scrollable = findScrollable(component);
+
+ if (scrollable != null) {
+ return scrollable.showOnScreen(context, component);
+ }
+
+ return false;
+ }
+
+ @Nullable
+ private static ScrollableComponent findScrollable(Component component) {
+ Component parent = component.getParent();
+
+ while (parent != null) {
ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);
if (scrollable != null) {
- scrollable.showOnScreen(context, component.getComponentId());
- return true;
+ return scrollable;
+ } else {
+ parent = parent.getParent();
}
}
- return false;
+ return null;
}
/**
@@ -173,6 +187,25 @@ public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibi
}
/**
+ * scroll content in a given direction
+ *
+ * @param context
+ * @param component
+ * @param direction
+ * @return
+ */
+ public boolean scrollDirection(
+ RemoteContext context, Component component, ScrollDirection direction) {
+ ScrollableComponent scrollable = component.selfOrModifier(ScrollableComponent.class);
+
+ if (scrollable != null) {
+ return scrollable.scrollDirection(context, direction);
+ }
+
+ return false;
+ }
+
+ /**
* Perform a click on the given component
*
* @param component
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index c38a44ac30be..da4e8d621602 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -39,6 +39,7 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
private final RemoteComposeDocumentAccessibility mRemoteDocA11y;
private final SemanticNodeApplier<AccessibilityNodeInfo> mApplier;
+ private final View mHost;
public PlatformRemoteComposeTouchHelper(
View host,
@@ -47,6 +48,7 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
super(host);
this.mRemoteDocA11y = remoteDocA11y;
this.mApplier = applier;
+ this.mHost = host;
}
public static PlatformRemoteComposeTouchHelper forRemoteComposePlayer(
@@ -150,6 +152,7 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
boolean performed = mRemoteDocA11y.performAction(component, action, arguments);
if (performed) {
+ mHost.invalidate();
invalidateRoot();
}
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 caf19e1ed34a..e5c20eb7ce18 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -73,7 +73,9 @@ public class CoreDocument implements Serializable {
// We also keep a more fine-grained BUILD number, exposed as
// ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
- static final float BUILD = 0.5f;
+ static final float BUILD = 0.6f;
+
+ private static final boolean UPDATE_VARIABLES_BEFORE_LAYOUT = false;
@NonNull ArrayList<Operation> mOperations = new ArrayList<>();
@@ -892,7 +894,10 @@ public class CoreDocument implements Serializable {
registerVariables(context, mOperations);
context.mMode = RemoteContext.ContextMode.UNSET;
- mFirstPaint = true;
+
+ if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+ mFirstPaint = true;
+ }
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -1241,11 +1246,13 @@ public class CoreDocument implements Serializable {
context.mRemoteComposeState = mRemoteComposeState;
context.mRemoteComposeState.setContext(context);
- // Update any dirty variables
- if (mFirstPaint) {
- mFirstPaint = false;
- } else {
- updateVariables(context, theme, mOperations);
+ if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+ // Update any dirty variables
+ if (mFirstPaint) {
+ mFirstPaint = false;
+ } else {
+ updateVariables(context, theme, mOperations);
+ }
}
// If we have a content sizing set, we are going to take the original document
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 ac9f98bd6b15..add9d5bae552 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -111,6 +111,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.managers
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
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.CollapsiblePriorityModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DrawContentOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
@@ -257,6 +258,7 @@ public class Operations {
public static final int MODIFIER_HEIGHT = 67;
public static final int MODIFIER_WIDTH_IN = 231;
public static final int MODIFIER_HEIGHT_IN = 232;
+ public static final int MODIFIER_COLLAPSIBLE_PRIORITY = 235;
public static final int MODIFIER_BACKGROUND = 55;
public static final int MODIFIER_BORDER = 107;
public static final int MODIFIER_PADDING = 58;
@@ -368,6 +370,7 @@ public class Operations {
map.put(MODIFIER_HEIGHT, HeightModifierOperation::read);
map.put(MODIFIER_WIDTH_IN, WidthInModifierOperation::read);
map.put(MODIFIER_HEIGHT_IN, HeightInModifierOperation::read);
+ map.put(MODIFIER_COLLAPSIBLE_PRIORITY, CollapsiblePriorityModifierOperation::read);
map.put(MODIFIER_PADDING, PaddingModifierOperation::read);
map.put(MODIFIER_BACKGROUND, BackgroundModifierOperation::read);
map.put(MODIFIER_BORDER, BorderModifierOperation::read);
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 e37833f33fa5..b297a023d03b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -46,7 +46,7 @@ public abstract class RemoteContext {
new CoreDocument(); // todo: is this a valid way to initialize? bbade@
public @NonNull RemoteComposeState mRemoteComposeState =
new RemoteComposeState(); // todo, is this a valid use of RemoteComposeState -- bbade@
-
+ private long mDocLoadTime = System.currentTimeMillis();
@Nullable protected PaintContext mPaintContext = null;
protected float mDensity = Float.NaN;
@@ -83,6 +83,20 @@ public abstract class RemoteContext {
}
}
+ /**
+ * Get the time the document was loaded
+ *
+ * @return time in ms since the document was loaded
+ */
+ public long getDocLoadTime() {
+ return mDocLoadTime;
+ }
+
+ /** Set the time the document was loaded */
+ public void setDocLoadTime() {
+ mDocLoadTime = System.currentTimeMillis();
+ }
+
public boolean isAnimationEnabled() {
return mAnimate;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
index e9cc26f58c9b..dee79a45de3d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
@@ -85,6 +85,9 @@ public class TimeAttribute extends PaintOperation {
/** the year */
public static final short TIME_YEAR = 12;
+ /** (value - doc_load_time) * 1E-3 */
+ public static final short TIME_FROM_LOAD_SEC = 14;
+
/**
* creates a new operation
*
@@ -226,6 +229,7 @@ public class TimeAttribute extends PaintOperation {
int val = mType & 255;
int flags = mType >> 8;
RemoteContext ctx = context.getContext();
+ long load_time = ctx.getDocLoadTime();
LongConstant longConstant = (LongConstant) ctx.getObject(mTimeId);
long value = longConstant.getValue();
long delta = 0;
@@ -292,6 +296,9 @@ public class TimeAttribute extends PaintOperation {
case TIME_YEAR:
ctx.loadFloat(mId, time.getYear());
break;
+ case TIME_FROM_LOAD_SEC:
+ ctx.loadFloat(mId, (value - load_time) * 1E-3f);
+ break;
}
}
@@ -334,6 +341,8 @@ public class TimeAttribute extends PaintOperation {
return "TIME_DAY_OF_WEEK";
case TIME_YEAR:
return "TIME_YEAR";
+ case TIME_FROM_LOAD_SEC:
+ return "TIME_FROM_LOAD_SEC";
default:
return "INVALID_TIME_TYPE";
}
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 f1158d91f94b..2a809c6f0a2a 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
@@ -824,15 +824,27 @@ public class Component extends PaintOperation
*
* @param value a 2 dimension float array that will receive the horizontal and vertical position
* of the component.
+ * @param forSelf whether the location is for this container or a child, relevant for scrollable
+ * items.
*/
- public void getLocationInWindow(@NonNull float[] value) {
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
value[0] += mX;
value[1] += mY;
if (mParent != null) {
- mParent.getLocationInWindow(value);
+ mParent.getLocationInWindow(value, false);
}
}
+ /**
+ * Returns the location of the component relative to the root component
+ *
+ * @param value a 2 dimension float array that will receive the horizontal and vertical position
+ * of the component.
+ */
+ public void getLocationInWindow(@NonNull float[] value) {
+ getLocationInWindow(value, true);
+ }
+
@NonNull
@Override
public String toString() {
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 6163d8099b8c..bc099e3a3b9d 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
@@ -289,11 +289,11 @@ public class LayoutComponent extends Component {
}
@Override
- public void getLocationInWindow(@NonNull float[] value) {
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
value[0] += mX + mPaddingLeft;
value[1] += mY + mPaddingTop;
if (mParent != null) {
- mParent.getLocationInWindow(value);
+ mParent.getLocationInWindow(value, false);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
index b0089525af5a..00ec60533087 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
@@ -24,10 +24,13 @@ 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.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+import java.util.ArrayList;
import java.util.List;
public class CollapsibleColumnLayout extends ColumnLayout {
@@ -153,7 +156,7 @@ public class CollapsibleColumnLayout extends ColumnLayout {
}
@Override
- protected boolean hasVerticalIntrinsicDimension() {
+ public boolean hasVerticalIntrinsicDimension() {
return true;
}
@@ -166,25 +169,72 @@ public class CollapsibleColumnLayout extends ColumnLayout {
boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
+ computeVisibleChildren(
+ context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
+ }
+
+ @Override
+ public void computeSize(
+ @NonNull PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ @NonNull MeasurePass measure) {
+ computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+ }
+
+ @Override
+ public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+ // if needed, take care of weight calculations
+ super.internalLayoutMeasure(context, measure);
+ // Check again for visibility
+ ComponentMeasure m = measure.get(this);
+ computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+ }
+
+ private void computeVisibleChildren(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @Nullable Size size) {
int visibleChildren = 0;
ComponentMeasure self = measure.get(this);
self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
float currentMaxHeight = maxHeight;
+ boolean hasPriorities = false;
for (Component c : mChildrenComponents) {
- if (c instanceof CollapsibleColumnLayout) {
- c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
- } else {
- c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+ if (!measure.contains(c.getComponentId())) {
+ // No need to remeasure here if already done
+ if (c instanceof CollapsibleColumnLayout) {
+ c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
+ } else {
+ c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+ }
}
+
ComponentMeasure m = measure.get(c);
if (!m.isGone()) {
- size.setWidth(Math.max(size.getWidth(), m.getW()));
- size.setHeight(size.getHeight() + m.getH());
+ if (size != null) {
+ size.setWidth(Math.max(size.getWidth(), m.getW()));
+ size.setHeight(size.getHeight() + m.getH());
+ }
visibleChildren++;
currentMaxHeight -= m.getH();
}
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null) {
+ hasPriorities = true;
+ }
+ }
}
- if (!mChildrenComponents.isEmpty()) {
+ if (!mChildrenComponents.isEmpty() && size != null) {
size.setHeight(size.getHeight() + (mSpacedBy * (visibleChildren - 1)));
}
@@ -192,7 +242,14 @@ public class CollapsibleColumnLayout extends ColumnLayout {
float childrenHeight = 0f;
boolean overflow = false;
- for (Component child : mChildrenComponents) {
+ ArrayList<Component> children = mChildrenComponents;
+ if (hasPriorities) {
+ // TODO: We need to cache this.
+ children =
+ CollapsiblePriority.sortWithPriorities(
+ mChildrenComponents, CollapsiblePriority.VERTICAL);
+ }
+ for (Component child : children) {
ComponentMeasure childMeasure = measure.get(child);
if (overflow || childMeasure.isGone()) {
childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
@@ -209,10 +266,10 @@ public class CollapsibleColumnLayout extends ColumnLayout {
visibleChildren++;
}
}
- if (verticalWrap) {
+ if (verticalWrap && size != null) {
size.setHeight(Math.min(maxHeight, childrenHeight));
}
- if (visibleChildren == 0 || size.getHeight() <= 0f) {
+ if (visibleChildren == 0 || (size != null && size.getHeight() <= 0f)) {
self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
new file mode 100644
index 000000000000..46cd45ecba8a
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 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.managers;
+
+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.modifiers.CollapsiblePriorityModifierOperation;
+
+import java.util.ArrayList;
+
+/** Utility class to manage collapsible priorities on components */
+public class CollapsiblePriority {
+
+ public static final int HORIZONTAL = 0;
+ public static final int VERTICAL = 1;
+
+ /**
+ * Returns the priority of a child component
+ *
+ * @param c the child component
+ * @return priority value, or 0f if not found
+ */
+ static float getPriority(Component c, int orientation) {
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null && priority.getOrientation() == orientation) {
+ return priority.getPriority();
+ }
+ }
+ return Float.MAX_VALUE;
+ }
+
+ /**
+ * Allocate and return a sorted array of components by their priorities
+ *
+ * @param components the children components
+ * @return list of components sorted by their priority in decreasing order
+ */
+ static ArrayList<Component> sortWithPriorities(
+ ArrayList<Component> components, int orientation) {
+ ArrayList<Component> sorted = new ArrayList<>(components);
+ sorted.sort(
+ (t1, t2) -> {
+ float p1 = getPriority(t1, orientation);
+ float p2 = getPriority(t2, orientation);
+ return (int) (p2 - p1);
+ });
+ return sorted;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
index 05f332960c16..e3632f9888ec 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
@@ -24,10 +24,13 @@ 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.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+import java.util.ArrayList;
import java.util.List;
public class CollapsibleRowLayout extends RowLayout {
@@ -135,8 +138,12 @@ public class CollapsibleRowLayout extends RowLayout {
}
@Override
- protected boolean hasHorizontalIntrinsicDimension() {
- return true;
+ public float minIntrinsicHeight(@NonNull RemoteContext context) {
+ float height = computeModifierDefinedHeight(context);
+ if (!mChildrenComponents.isEmpty()) {
+ height += mChildrenComponents.get(0).minIntrinsicHeight(context);
+ }
+ return height;
}
@Override
@@ -149,12 +156,8 @@ public class CollapsibleRowLayout extends RowLayout {
}
@Override
- public float minIntrinsicHeight(@NonNull RemoteContext context) {
- float height = computeModifierDefinedHeight(context);
- if (!mChildrenComponents.isEmpty()) {
- height += mChildrenComponents.get(0).minIntrinsicHeight(context);
- }
- return height;
+ public boolean hasHorizontalIntrinsicDimension() {
+ return true;
}
@Override
@@ -166,45 +169,107 @@ public class CollapsibleRowLayout extends RowLayout {
boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
- super.computeWrapSize(
- context, Float.MAX_VALUE, maxHeight, horizontalWrap, verticalWrap, measure, size);
+ computeVisibleChildren(
+ context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
}
@Override
- public boolean applyVisibility(
- float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
- float childrenWidth = 0f;
- float childrenHeight = 0f;
- boolean changedVisibility = false;
+ public void computeSize(
+ @NonNull PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ @NonNull MeasurePass measure) {
+ computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+ }
+
+ @Override
+ public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+ // if needed, take care of weight calculations
+ super.internalLayoutMeasure(context, measure);
+ // Check again for visibility
+ ComponentMeasure m = measure.get(this);
+ computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+ }
+
+ private void computeVisibleChildren(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @Nullable Size size) {
int visibleChildren = 0;
ComponentMeasure self = measure.get(this);
- self.clearVisibilityOverride();
- if (selfWidth <= 0 || selfHeight <= 0) {
- self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
- return true;
+ self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
+ float currentMaxWidth = maxWidth;
+ boolean hasPriorities = false;
+ for (Component c : mChildrenComponents) {
+ if (!measure.contains(c.getComponentId())) {
+ // No need to remeasure here if already done
+ if (c instanceof CollapsibleRowLayout) {
+ c.measure(context, 0f, currentMaxWidth, 0f, maxHeight, measure);
+ } else {
+ c.measure(context, 0f, Float.MAX_VALUE, 0f, maxHeight, measure);
+ }
+ }
+ ComponentMeasure m = measure.get(c);
+ if (!m.isGone()) {
+ if (size != null) {
+ size.setHeight(Math.max(size.getHeight(), m.getH()));
+ size.setWidth(size.getWidth() + m.getW());
+ }
+ visibleChildren++;
+ currentMaxWidth -= m.getW();
+ }
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null) {
+ hasPriorities = true;
+ }
+ }
+ }
+ if (!mChildrenComponents.isEmpty() && size != null) {
+ size.setWidth(size.getWidth() + (mSpacedBy * (visibleChildren - 1)));
}
- for (Component child : mChildrenComponents) {
+
+ float childrenWidth = 0f;
+ float childrenHeight = 0f;
+
+ boolean overflow = false;
+ ArrayList<Component> children = mChildrenComponents;
+ if (hasPriorities) {
+ // TODO: We need to cache this.
+ children =
+ CollapsiblePriority.sortWithPriorities(
+ mChildrenComponents, CollapsiblePriority.HORIZONTAL);
+ }
+ for (Component child : children) {
ComponentMeasure childMeasure = measure.get(child);
- int visibility = childMeasure.getVisibility();
- childMeasure.clearVisibilityOverride();
- if (!childMeasure.isVisible()) {
+ if (overflow || childMeasure.isGone()) {
+ childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
continue;
}
- if (childrenWidth + childMeasure.getW() > selfWidth
- && childrenHeight + childMeasure.getH() > selfHeight) {
+ float childWidth = childMeasure.getW();
+ boolean childDoesNotFits = childrenWidth + childWidth > maxWidth;
+ if (childDoesNotFits) {
childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
- if (visibility != childMeasure.getVisibility()) {
- changedVisibility = true;
- }
+ overflow = true;
} else {
- childrenWidth += childMeasure.getW();
+ childrenWidth += childWidth;
childrenHeight = Math.max(childrenHeight, childMeasure.getH());
visibleChildren++;
}
}
- if (visibleChildren == 0) {
+ if (horizontalWrap && size != null) {
+ size.setWidth(Math.min(maxWidth, childrenWidth));
+ }
+ if (visibleChildren == 0 || (size != null && size.getWidth() <= 0f)) {
self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
}
- return changedVisibility;
}
}
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 cda90c2d3b0b..9566242ccbc5 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
@@ -33,6 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
@@ -372,6 +373,17 @@ public class ColumnLayout extends LayoutManager {
DebugLog.e();
}
+ @Override
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+ super.getLocationInWindow(value, forSelf);
+
+ if (!forSelf && mVerticalScrollDelegate instanceof ScrollModifierOperation) {
+ ScrollModifierOperation smo = (ScrollModifierOperation) mVerticalScrollDelegate;
+
+ value[1] += smo.getScrollY();
+ }
+ }
+
/**
* The name of the class
*
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 5b66b95cf1dd..eb10ead34781 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
@@ -226,9 +226,17 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
measure,
mCachedWrapSize);
float w = mCachedWrapSize.getWidth();
- computeSize(context, 0f, w, 0, measuredHeight, measure);
if (hasHorizontalScroll()) {
+ computeSize(context, 0f, w, 0, measuredHeight, measure);
mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
+ } else {
+ computeSize(
+ context,
+ 0f,
+ Math.min(measuredWidth, insetMaxWidth),
+ 0,
+ Math.min(measuredHeight, insetMaxHeight),
+ measure);
}
} else if (hasVerticalIntrinsicDimension()) {
mCachedWrapSize.setWidth(0f);
@@ -236,9 +244,17 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
computeWrapSize(
context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize);
float h = mCachedWrapSize.getHeight();
- computeSize(context, 0f, measuredWidth, 0, h, measure);
if (hasVerticalScroll()) {
+ computeSize(context, 0f, measuredWidth, 0, h, measure);
mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+ } else {
+ computeSize(
+ context,
+ 0f,
+ Math.min(measuredWidth, insetMaxWidth),
+ 0,
+ Math.min(measuredHeight, insetMaxHeight),
+ measure);
}
} else {
float maxChildWidth = measuredWidth - mPaddingLeft - mPaddingRight;
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 d5d2e03c3f2a..15b54a3ce994 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
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LayoutCo
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
@@ -386,6 +387,17 @@ public class RowLayout extends LayoutManager {
DebugLog.e();
}
+ @Override
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+ super.getLocationInWindow(value, forSelf);
+
+ if (!forSelf && mHorizontalScrollDelegate instanceof ScrollModifierOperation) {
+ ScrollModifierOperation smo = (ScrollModifierOperation) mHorizontalScrollDelegate;
+
+ value[0] += smo.getScrollX();
+ }
+ }
+
/**
* The name of the class
*
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 d383ee9e4fc9..120c740eccda 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
@@ -77,6 +77,7 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
private final Size mCachedSize = new Size(0f, 0f);
@Nullable private String mCachedString = "";
+ @Nullable private String mNewString;
Platform.ComputedTextLayout mComputedTextLayout;
@@ -99,7 +100,7 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
if (cachedString != null && cachedString.equalsIgnoreCase(mCachedString)) {
return;
}
- mCachedString = cachedString;
+ mNewString = cachedString;
if (mType == -1) {
if (mFontFamilyId != -1) {
String fontFamily = context.getText(mFontFamilyId);
@@ -119,8 +120,6 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
mType = 0;
}
}
- mTextW = -1;
- mTextH = -1;
if (mHorizontalScrollDelegate != null) {
mHorizontalScrollDelegate.reset();
@@ -351,6 +350,9 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
mPaint.setColor(mColor);
context.replacePaint(mPaint);
float[] bounds = new float[4];
+ if (mNewString != null && !mNewString.equals(mCachedString)) {
+ mCachedString = mNewString;
+ }
if (mCachedString == null) {
return;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
new file mode 100644
index 000000000000..b1f2d2d35b93
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 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.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.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
+
+import java.util.List;
+
+/** Set an optional priority on a component within a collapsible layout */
+public class CollapsiblePriorityModifierOperation extends Operation
+ implements ModifierOperation, Serializable {
+ private static final int OP_CODE = Operations.MODIFIER_COLLAPSIBLE_PRIORITY;
+ public static final String CLASS_NAME = "CollapsiblePriorityModifierOperation";
+
+ private float mPriority;
+ private int mOrientation;
+
+ public CollapsiblePriorityModifierOperation(int orientation, float priority) {
+ mOrientation = orientation;
+ mPriority = priority;
+ }
+
+ public float getPriority() {
+ return mPriority;
+ }
+
+ public int getOrientation() {
+ return mOrientation;
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mOrientation, mPriority);
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ // nothing
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return "";
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int orientation = buffer.readInt();
+ float priority = buffer.readFloat();
+ operations.add(new CollapsiblePriorityModifierOperation(orientation, priority));
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * 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, "CollapsiblePriorityModifier")
+ .description("Add additional priority to children of Collapsible layouts")
+ .field(DocumentedOperation.INT, "orientation", "Horizontal(0) or Vertical (1)")
+ .field(DocumentedOperation.FLOAT, "priority", "The associated priority");
+ }
+
+ /**
+ * Writes out the CollapsiblePriorityModifier to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param priority priority value
+ * @param orientation orientation (HORIZONTAL or VERTICAL)
+ */
+ public static void apply(@NonNull WireBuffer buffer, int orientation, float priority) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(orientation);
+ buffer.writeFloat(priority);
+ }
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ serializer
+ .addTags(SerializeTags.MODIFIER)
+ .addType(name())
+ .add("orientation", mOrientation)
+ .add("priority", mPriority);
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, "PRIORITY = [" + getPriority() + "] (" + mOrientation + ")");
+ }
+}
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 3e1f32de66e4..42692f95fcda 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
@@ -430,9 +430,35 @@ public class ScrollModifierOperation extends ListActionsOperation
}
@Override
- public boolean showOnScreen(RemoteContext context, int childId) {
- // TODO correct this when we trust the bounds in parent
- return scrollByOffset(context, -1000) != 0;
+ public boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+ float offset = mHostDimension * 0.7f;
+
+ if (direction == ScrollDirection.FORWARD
+ || direction == ScrollDirection.DOWN
+ || direction == ScrollDirection.RIGHT) {
+ offset *= -1;
+ }
+
+ return scrollByOffset(context, (int) offset) != 0;
+ }
+
+ @Override
+ public boolean showOnScreen(RemoteContext context, Component child) {
+ float[] locationInWindow = new float[2];
+ child.getLocationInWindow(locationInWindow);
+
+ int offset = 0;
+ if (handlesVerticalScroll()) {
+ offset = (int) -locationInWindow[1];
+ } else {
+ offset = (int) -locationInWindow[0];
+ }
+
+ if (offset == 0) {
+ return true;
+ } else {
+ return scrollByOffset(context, offset) != 0;
+ }
}
@Nullable
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index a95a175d0edd..120c7ac9efbf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -35,7 +35,10 @@ public class StringUtils {
@NonNull
public static String floatToString(
float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
-
+ boolean isNeg = value < 0;
+ if (isNeg) {
+ value = -value;
+ }
int integerPart = (int) value;
float fractionalPart = value % 1;
@@ -54,14 +57,13 @@ public class StringUtils {
integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
}
if (afterDecimalPoint == 0) {
- return integerPartString;
+ return ((isNeg) ? "-" : "") + integerPartString;
}
// Convert fractional part to string and pad with zeros
for (int i = 0; i < afterDecimalPoint; i++) {
fractionalPart *= 10;
}
-
fractionalPart = Math.round(fractionalPart);
for (int i = 0; i < afterDecimalPoint; i++) {
@@ -87,6 +89,6 @@ public class StringUtils {
fact = fact + new String(c);
}
- return integerPartString + "." + fact;
+ return ((isNeg) ? "-" : "") + integerPartString + "." + fact;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
index 3d1bd12357c9..1610e6332c1c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
@@ -18,6 +18,7 @@ package com.android.internal.widget.remotecompose.core.semantics;
import android.annotation.Nullable;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
/**
* Interface for components that support scrolling.
@@ -48,13 +49,23 @@ public interface ScrollableComponent extends AccessibilitySemantics {
}
/**
+ * Scrolls the content in the specified direction.
+ *
+ * @param direction the direction to scroll
+ * @return whether a scroll was possible
+ */
+ default boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+ return false;
+ }
+
+ /**
* Show a child with the given ID on the screen, typically scrolling so it's fully on screen.
*
- * @param childId The ID of the child to check for visibility.
+ * @param child The child (including nested) to check for visibility.
* @return {@code true} if the child with the given ID could be shown on screen; {@code false}
* otherwise.
*/
- default boolean showOnScreen(RemoteContext context, int childId) {
+ default boolean showOnScreen(RemoteContext context, Component child) {
return false;
}
@@ -108,4 +119,13 @@ public interface ScrollableComponent extends AccessibilitySemantics {
return mCanScrollBackwards;
}
}
+
+ enum ScrollDirection {
+ FORWARD,
+ BACKWARD,
+ UP,
+ DOWN,
+ LEFT,
+ RIGHT,
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index e1f2924021a4..575a6b2ee518 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -22,7 +22,6 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.TouchListener;
import com.android.internal.widget.remotecompose.core.VariableSupport;
@@ -43,7 +42,6 @@ import java.util.HashMap;
*
* <p>This is used to play the RemoteCompose operations on Android.
*/
-@VisibleForTesting
public class AndroidRemoteContext extends RemoteContext {
public void useCanvas(Canvas canvas) {
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 0bc99abc17bc..17f4fc82af5f 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
@@ -102,6 +102,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mDocument = value;
mDocument.initializeContext(mARContext);
mDisable = false;
+ mARContext.setDocLoadTime();
mARContext.setAnimationEnabled(true);
mARContext.setDensity(mDensity);
mARContext.setUseChoreographer(true);