summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java157
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/PaintContext.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java36
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java54
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Header.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java67
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java100
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/Container.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ContainerEnd.java (renamed from core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java)6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java93
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java34
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java426
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java46
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java87
37 files changed, 712 insertions, 613 deletions
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 33f93fccff58..1bd502df8e57 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -21,26 +21,20 @@ 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.Header;
import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.Theme;
-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.ComponentEnd;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.Container;
+import com.android.internal.widget.remotecompose.core.operations.layout.ContainerEnd;
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.TouchCancelModifierOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.TouchDownModifierOperation;
-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;
@@ -57,7 +51,18 @@ import java.util.Set;
public class CoreDocument {
private static final boolean DEBUG = false;
- private static final int DOCUMENT_API_LEVEL = 2;
+
+ // Semantic version
+ public static final int MAJOR_VERSION = 0;
+ public static final int MINOR_VERSION = 3;
+ public static final int PATCH_VERSION = 0;
+
+ // Internal version level
+ public static final int DOCUMENT_API_LEVEL = 3;
+
+ // We also keep a more fine-grained BUILD number, exposed as
+ // ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
+ static final float BUILD = 0.0f;
@NonNull ArrayList<Operation> mOperations = new ArrayList<>();
@@ -65,8 +70,9 @@ public class CoreDocument {
@NonNull RemoteComposeState mRemoteComposeState = new RemoteComposeState();
@VisibleForTesting @NonNull public TimeVariables mTimeVariables = new TimeVariables();
+
// Semantic version of the document
- @NonNull Version mVersion = new Version(0, 1, 0);
+ @NonNull Version mVersion = new Version(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION);
@Nullable
String mContentDescription; // text description of the document (used for accessibility)
@@ -551,6 +557,11 @@ public class CoreDocument {
mOperations = new ArrayList<Operation>();
buffer.inflateFromBuffer(mOperations);
for (Operation op : mOperations) {
+ if (op instanceof Header) {
+ // Make sure we parse the version at init time...
+ Header header = (Header) op;
+ header.setVersion(this);
+ }
if (op instanceof IntegerExpression) {
IntegerExpression expression = (IntegerExpression) op;
mIntegerExpressions.put((long) expression.mId, expression);
@@ -581,85 +592,49 @@ public class CoreDocument {
*/
@NonNull
private ArrayList<Operation> inflateComponents(@NonNull ArrayList<Operation> operations) {
- Component currentComponent = null;
- ArrayList<Component> components = new ArrayList<>();
ArrayList<Operation> finalOperationsList = new ArrayList<>();
ArrayList<Operation> ops = finalOperationsList;
- ClickModifierOperation currentClickModifier = null;
- TouchDownModifierOperation currentTouchDownModifier = null;
- TouchUpModifierOperation currentTouchUpModifier = null;
- TouchCancelModifierOperation currentTouchCancelModifier = null;
- LoopOperation currentLoop = null;
- ScrollModifierOperation currentScrollModifier = null;
+
+ ArrayList<Container> containers = new ArrayList<>();
mLastId = -1;
for (Operation o : operations) {
- if (o instanceof ComponentStartOperation) {
- Component component = (Component) o;
- component.setParent(currentComponent);
- components.add(component);
- currentComponent = component;
- ops.add(currentComponent);
- ops = currentComponent.getList();
- if (component.getComponentId() < mLastId) {
- mLastId = component.getComponentId();
- }
- } else if (o instanceof ComponentEnd) {
- if (currentComponent != null) {
- currentComponent.inflate();
+ if (o instanceof Container) {
+ Container container = (Container) o;
+ containers.add(container);
+ ops = container.getList();
+ if (container instanceof Component) {
+ Component component = (Component) container;
+ if (component.getComponentId() < mLastId) {
+ mLastId = component.getComponentId();
+ }
}
- components.remove(components.size() - 1);
- if (!components.isEmpty()) {
- currentComponent = components.get(components.size() - 1);
- ops = currentComponent.getList();
- } else {
- ops = finalOperationsList;
+ } else if (o instanceof ContainerEnd) {
+ // check if we have a parent container
+ Container container = null;
+ // pop the container
+ if (!containers.isEmpty()) {
+ container = containers.remove(containers.size() - 1);
}
- } else if (o instanceof ClickModifierOperation) {
- // TODO: refactor to add container <- component...
- currentClickModifier = (ClickModifierOperation) o;
- ops = currentClickModifier.getList();
- } else if (o instanceof TouchDownModifierOperation) {
- currentTouchDownModifier = (TouchDownModifierOperation) o;
- ops = currentTouchDownModifier.getList();
- } else if (o instanceof TouchUpModifierOperation) {
- currentTouchUpModifier = (TouchUpModifierOperation) o;
- ops = currentTouchUpModifier.getList();
- } 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) {
- ops.add(currentClickModifier);
- currentClickModifier = null;
- } else if (currentTouchDownModifier != null) {
- ops.add(currentTouchDownModifier);
- currentTouchDownModifier = null;
- } else if (currentTouchUpModifier != null) {
- ops.add(currentTouchUpModifier);
- currentTouchUpModifier = null;
- } else if (currentTouchCancelModifier != null) {
- ops.add(currentTouchCancelModifier);
- currentTouchCancelModifier = null;
- } else if (currentScrollModifier != null) {
- ops.add(currentScrollModifier);
- currentScrollModifier = null;
+ Container parentContainer = null;
+ if (!containers.isEmpty()) {
+ parentContainer = containers.get(containers.size() - 1);
}
- } else if (o instanceof LoopOperation) {
- currentLoop = (LoopOperation) o;
- ops = currentLoop.getList();
- } else if (o instanceof LoopEnd) {
- if (currentComponent != null) {
- ops = currentComponent.getList();
- ops.add(currentLoop);
+ if (parentContainer != null) {
+ ops = parentContainer.getList();
} else {
ops = finalOperationsList;
}
- currentLoop = null;
+ if (container != null) {
+ if (container instanceof Component) {
+ Component component = (Component) container;
+ if (parentContainer instanceof Component) {
+ component.setParent((Component) parentContainer);
+ }
+ component.inflate();
+ }
+ ops.add((Operation) container);
+ }
} else {
ops.add(o);
}
@@ -744,7 +719,7 @@ public class CoreDocument {
* @param minorVersion minor version number, increased when adding new features
* @param patch patch level, increased upon bugfixes
*/
- void setVersion(int majorVersion, int minorVersion, int patch) {
+ public void setVersion(int majorVersion, int minorVersion, int patch) {
mVersion = new Version(majorVersion, minorVersion, patch);
}
@@ -1080,19 +1055,22 @@ public class CoreDocument {
}
}
context.mMode = RemoteContext.ContextMode.PAINT;
- for (Operation op : mOperations) {
+ for (int i = 0; i < mOperations.size(); i++) {
+ Operation op = mOperations.get(i);
// operations will only be executed if no theme is set (ie UNSPECIFIED)
// or the theme is equal as the one passed in argument to paint.
boolean apply = true;
if (theme != Theme.UNSPECIFIED) {
+ int currentTheme = context.getTheme();
apply =
- op instanceof Theme // always apply a theme setter
- || context.getTheme() == theme
- || context.getTheme() == Theme.UNSPECIFIED;
+ currentTheme == theme
+ || currentTheme == Theme.UNSPECIFIED
+ || op instanceof Theme; // always apply a theme setter
}
if (apply) {
- if (op.isDirty() || op instanceof PaintOperation) {
- if (op.isDirty() && op instanceof VariableSupport) {
+ boolean opIsDirty = op.isDirty();
+ if (opIsDirty || op instanceof PaintOperation) {
+ if (opIsDirty && op instanceof VariableSupport) {
op.markNotDirty();
((VariableSupport) op).updateVariables(context);
}
@@ -1253,8 +1231,11 @@ public class CoreDocument {
private void toNestedString(
@NonNull Component base, @NonNull StringBuilder ret, String indent) {
for (Operation mOperation : base.mList) {
- ret.append(mOperation.toString());
- ret.append("\n");
+ for (String line : mOperation.toString().split("\n")) {
+ ret.append(indent);
+ ret.append(line);
+ ret.append("\n");
+ }
if (mOperation instanceof Component) {
toNestedString((Component) mOperation, ret, indent + " ");
}
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 04e490fa5214..d9f12cb71d53 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -73,12 +73,10 @@ import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
+import com.android.internal.widget.remotecompose.core.operations.layout.ContainerEnd;
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.TouchCancelModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.TouchDownModifierOperation;
@@ -208,7 +206,6 @@ public class Operations {
public static final int LAYOUT_STATE = 217;
public static final int COMPONENT_START = 2;
- public static final int COMPONENT_END = 3;
public static final int MODIFIER_WIDTH = 16;
public static final int MODIFIER_HEIGHT = 67;
@@ -223,7 +220,7 @@ public class Operations {
public static final int MODIFIER_TOUCH_UP = 220;
public static final int MODIFIER_TOUCH_CANCEL = 225;
- public static final int OPERATIONS_LIST_END = 214;
+ public static final int CONTAINER_END = 214;
public static final int MODIFIER_OFFSET = 221;
public static final int MODIFIER_ZINDEX = 223;
@@ -233,7 +230,6 @@ public class Operations {
public static final int MODIFIER_RIPPLE = 229;
public static final int LOOP_START = 215;
- public static final int LOOP_END = 216;
public static final int MODIFIER_VISIBILITY = 211;
public static final int HOST_ACTION = 209;
@@ -311,12 +307,10 @@ public class Operations {
map.put(TEXT_LOOKUP_INT, TextLookupInt::read);
map.put(LOOP_START, LoopOperation::read);
- map.put(LOOP_END, LoopEnd::read);
// Layout
map.put(COMPONENT_START, ComponentStart::read);
- map.put(COMPONENT_END, ComponentEnd::read);
map.put(ANIMATION_SPEC, AnimationSpec::read);
map.put(MODIFIER_WIDTH, WidthModifierOperation::read);
@@ -338,7 +332,7 @@ public class Operations {
map.put(MODIFIER_MARQUEE, MarqueeModifierOperation::read);
map.put(MODIFIER_RIPPLE, RippleModifierOperation::read);
- map.put(OPERATIONS_LIST_END, OperationsListEnd::read);
+ map.put(CONTAINER_END, ContainerEnd::read);
map.put(HOST_ACTION, HostActionOperation::read);
map.put(HOST_NAMED_ACTION, HostNamedActionOperation::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index 7ecd11826303..4a40a31ef6ab 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -16,6 +16,7 @@
package com.android.internal.widget.remotecompose.core;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
@@ -297,4 +298,12 @@ public abstract class PaintContext {
public boolean isVisualDebug() {
return mContext.isVisualDebug();
}
+
+ /**
+ * Returns a String from an id
+ *
+ * @param textID
+ * @return the string if found
+ */
+ public abstract @Nullable String getText(int textID);
}
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 0ae7a94b948d..fc1e36d45a81 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -75,12 +75,10 @@ import com.android.internal.widget.remotecompose.core.operations.Theme;
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.CanvasContent;
-import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
+import com.android.internal.widget.remotecompose.core.operations.layout.ContainerEnd;
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;
@@ -734,6 +732,22 @@ public class RemoteComposeBuffer {
}
/**
+ * Draw the text, with origin at (x,y) along the specified path.
+ *
+ * @param textId The text to be drawn
+ * @param path The path the text should follow for its baseline
+ * @param hOffset The distance along the path to add to the text's starting position
+ * @param vOffset The distance above(-) or below(+) the path to position the text
+ */
+ public void addDrawTextOnPath(int textId, Object path, float hOffset, float vOffset) {
+ int pathId = mRemoteComposeState.dataGetId(path);
+ if (pathId == -1) { // never been seen before
+ pathId = addPathData(path);
+ }
+ DrawTextOnPath.apply(mBuffer, textId, pathId, hOffset, vOffset);
+ }
+
+ /**
* Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
* the paint.
*
@@ -1646,6 +1660,16 @@ public class RemoteComposeBuffer {
}
/**
+ * This defines the name of the float given the id
+ *
+ * @param id of the float
+ * @param name name of the float
+ */
+ public void setFloatName(int id, String name) {
+ NamedVariable.apply(mBuffer, id, NamedVariable.FLOAT_TYPE, name);
+ }
+
+ /**
* Returns a usable component id -- either the one passed in parameter if not -1 or a generated
* one.
*
@@ -1685,7 +1709,7 @@ public class RemoteComposeBuffer {
/** Add a component end tag */
public void addComponentEnd() {
- ComponentEnd.apply(mBuffer);
+ ContainerEnd.apply(mBuffer);
}
/**
@@ -1718,7 +1742,7 @@ public class RemoteComposeBuffer {
new float[] {notches, notchMax},
null);
- OperationsListEnd.apply(mBuffer);
+ ContainerEnd.apply(mBuffer);
}
/**
@@ -1886,7 +1910,7 @@ public class RemoteComposeBuffer {
}
public void addLoopEnd() {
- LoopEnd.apply(mBuffer);
+ ContainerEnd.apply(mBuffer);
}
public void addStateLayout(
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 5c3df7e95a1f..cd26198caf2e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -318,7 +318,8 @@ public class RemoteComposeState implements CollectionsAccess {
private void updateListeners(int id) {
ArrayList<VariableSupport> v = mVarListeners.get(id);
if (v != null && mRemoteContext != null) {
- for (VariableSupport c : v) {
+ for (int i = 0; i < v.size(); i++) {
+ VariableSupport c = v.get(i);
c.markDirty();
}
}
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 b5587ce095a2..63469aabaed5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -65,6 +65,8 @@ public abstract class RemoteContext {
public @Nullable Component mLastComponent;
public long currentTime = 0L;
+ private boolean mUseChoreographer = true;
+
public float getDensity() {
return mDensity;
}
@@ -187,6 +189,40 @@ public abstract class RemoteContext {
public abstract void clearNamedIntegerOverride(@NonNull String integerName);
/**
+ * Set the value of a named float. This overrides the float in the document
+ *
+ * @param floatName the name of the float to override
+ * @param value Override the default float
+ */
+ public abstract void setNamedFloatOverride(String floatName, float value);
+
+ /**
+ * Allows to clear a named Float.
+ *
+ * <p>If an override exists, we revert back to the default value in the document.
+ *
+ * @param floatName the name of the float to override
+ */
+ public abstract void clearNamedFloatOverride(String floatName);
+
+ /**
+ * Set the value of a named Object. This overrides the Object in the document
+ *
+ * @param dataName the name of the Object to override
+ * @param value Override the default float
+ */
+ public abstract void setNamedDataOverride(String dataName, Object value);
+
+ /**
+ * Allows to clear a named Object.
+ *
+ * <p>If an override exists, we revert back to the default value in the document.
+ *
+ * @param dataName the name of the Object to override
+ */
+ public abstract void clearNamedDataOverride(String dataName);
+
+ /**
* Support Collections by registering this collection
*
* @param id id of the collection
@@ -224,6 +260,24 @@ public abstract class RemoteContext {
}
/**
+ * Returns true if we should use the choreographter
+ *
+ * @return true if we use the choreographer
+ */
+ public boolean useChoreographer() {
+ return mUseChoreographer;
+ }
+
+ /**
+ * Set to true to use the android choreographer
+ *
+ * @param value
+ */
+ public void setUseChoreographer(boolean value) {
+ mUseChoreographer = value;
+ }
+
+ /**
* 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
* loading a bitmap) should execute - PAINT : only operations painting should execute
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 ea917db98e65..cd5b202f5a79 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -24,7 +24,6 @@ import java.time.ZoneOffset;
/** This generates the standard system variables for time. */
public class TimeVariables {
- private static final float BUILD = 0.02f;
/**
* This class populates all time variables in the system
@@ -59,7 +58,9 @@ public class TimeVariables {
context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
context.loadFloat(RemoteContext.ID_DAY_OF_MONTH, month);
context.loadFloat(RemoteContext.ID_WEEK_DAY, day_week);
- context.loadFloat(RemoteContext.ID_API_LEVEL, CoreDocument.getDocumentApiLevel() + BUILD);
+ context.loadFloat(
+ RemoteContext.ID_API_LEVEL,
+ CoreDocument.getDocumentApiLevel() + CoreDocument.BUILD);
}
/**
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 f839922b25e2..5d0c43723ea1 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
@@ -44,8 +44,11 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
float mOutPanX;
float mOutPanY;
+ String mLastString;
+
public static final int ANCHOR_TEXT_RTL = 1;
public static final int ANCHOR_MONOSPACE_MEASURE = 2;
+ public static final int MEASURE_EVERY_TIME = 4;
public DrawTextAnchored(int textID, float x, float y, float panX, float panY, int flags) {
mTextID = textID;
@@ -224,7 +227,13 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH
: 0;
- context.getTextBounds(mTextID, 0, -1, flags, mBounds);
+
+ String str = context.getText(mTextID);
+ if (str != mLastString || (mFlags & MEASURE_EVERY_TIME) != 0) {
+ mLastString = str;
+ context.getTextBounds(mTextID, 0, -1, flags, mBounds);
+ }
+
float x = mOutX + getHorizontalOffset();
float y = Float.isNaN(mOutPanY) ? mOutY : mOutY + getVerticalOffset();
context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, (mFlags & ANCHOR_TEXT_RTL) == 1);
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 c1872fd0fed0..e09745aa8546 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
@@ -151,21 +151,18 @@ 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;
+ float lastComputedValue = mFloatAnimation.get(t - mLastChange);
if (lastComputedValue != mLastAnimatedValue) {
mLastAnimatedValue = lastComputedValue;
+ context.loadFloat(mId, lastComputedValue);
context.needsRepaint();
}
} else if (mSpring != null) {
- float f = mSpring.get(t - mLastChange);
- context.loadFloat(mId, f);
- lastComputedValue = f;
+ float lastComputedValue = mSpring.get(t - mLastChange);
if (lastComputedValue != mLastAnimatedValue) {
mLastAnimatedValue = lastComputedValue;
+ context.loadFloat(mId, lastComputedValue);
context.needsRepaint();
}
} else {
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 656dc09c396f..4c9602572721 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
@@ -15,11 +15,15 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import static com.android.internal.widget.remotecompose.core.CoreDocument.MAJOR_VERSION;
+import static com.android.internal.widget.remotecompose.core.CoreDocument.MINOR_VERSION;
+import static com.android.internal.widget.remotecompose.core.CoreDocument.PATCH_VERSION;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
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.RemoteComposeOperation;
@@ -38,9 +42,6 @@ import java.util.List;
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 = 2;
- public static final int PATCH_VERSION = 0;
int mMajorVersion;
int mMinorVersion;
@@ -191,4 +192,8 @@ public class Header extends Operation implements RemoteComposeOperation {
// .field(FLOAT, "DENSITY", "Major version")
.field(LONG, "CAPABILITIES", "Major version");
}
+
+ public void setVersion(CoreDocument document) {
+ document.setVersion(mMajorVersion, mMinorVersion, mPatchVersion);
+ }
}
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 14b72af84e66..3b293bd1b8e0 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
@@ -97,10 +97,10 @@ public class TouchExpression extends Operation implements VariableSupport, Touch
/** 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 */
+ /** Stop at a collection of point described in absolute cordnates */
public static final int STOP_NOTCHES_ABSOLUTE = 5;
- /** Jump to the absloute poition of the point */
+ /** Jump to the absolute poition of the point */
public static final int STOP_ABSOLUTE_POS = 6;
/**
@@ -112,9 +112,9 @@ public class TouchExpression extends Operation implements VariableSupport, Touch
* @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 velocityId the velocity (not used)
+ * @param stopMode the behaviour on touch oup
+ * @param stopSpec the parameters that affect the touch up behaviour
* @param easingSpec the easing parameters for coming to a stop
*/
public TouchExpression(
@@ -249,10 +249,10 @@ public class TouchExpression extends Operation implements VariableSupport, Touch
}
private float getStopPosition(float pos, float slope) {
- float target = pos + slope / mMaxAcceleration;
+ float target = pos + slope / 2f;
if (mWrapMode) {
pos = wrap(pos);
- target = pos += +slope / mMaxAcceleration;
+ target = pos += +slope / 2f;
} else {
target = Math.max(Math.min(target, mOutMax), mOutMin);
}
@@ -268,7 +268,6 @@ public class TouchExpression extends Operation implements VariableSupport, Touch
int evenSpacing = (int) mOutStopSpec[0];
float notchMax = (mOutStopSpec.length > 1) ? mOutStopSpec[1] : mOutMax;
float step = (notchMax - min) / evenSpacing;
-
float notch = min + step * (int) (0.5f + (target - mOutMin) / step);
if (!mWrapMode) {
notch = Math.max(Math.min(notch, mOutMax), min);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index de43b90840cc..bd68d5a8c180 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -41,6 +41,16 @@ public class Utils {
}
/**
+ * Converts an id encoded in a float to the corresponding long id.
+ *
+ * @param value the float if to convert
+ * @return the float id converted to a long id
+ */
+ public static long longIdFromNan(float value) {
+ return ((long) idFromNan(value)) + 0x100000000L;
+ }
+
+ /**
* convert a long into an ID
*
* @param v the long to convert
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 652ab2bc1cbc..143223398a2a 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
@@ -26,11 +26,13 @@ public class AnimatableValue {
int mId = 0;
float mValue = 0f;
+ boolean mAnimateValueChanges = true;
boolean mAnimate = false;
long mAnimateTargetTime = 0;
float mAnimateDuration = 300f;
float mTargetRotationX;
float mStartRotationX;
+ long mLastUpdate = 0L;
int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
FloatAnimation mMotionEasing;
@@ -39,8 +41,10 @@ public class AnimatableValue {
* Value to animate
*
* @param value value
+ * @param animateValueChanges animate the change of values
*/
- public AnimatableValue(float value) {
+ public AnimatableValue(float value, boolean animateValueChanges) {
+ mAnimateValueChanges = animateValueChanges;
if (Utils.isVariable(value)) {
mId = Utils.idFromNan(value);
mIsVariable = true;
@@ -50,6 +54,15 @@ public class AnimatableValue {
}
/**
+ * Value to animate.
+ *
+ * @param value value
+ */
+ public AnimatableValue(float value) {
+ this(value, true);
+ }
+
+ /**
* Get the value
*
* @return the value
@@ -69,29 +82,41 @@ public class AnimatableValue {
return mValue;
}
float value = context.getContext().mRemoteComposeState.getFloat(mId);
-
- if (value != mValue && !mAnimate) {
- // animate
- mStartRotationX = mValue;
- mTargetRotationX = value;
- mAnimate = true;
- mAnimateTargetTime = System.currentTimeMillis();
- mMotionEasing =
- new FloatAnimation(
- mMotionEasingType, mAnimateDuration / 1000f, null, 0f, Float.NaN);
- mMotionEasing.setTargetValue(1f);
- }
- if (mAnimate) {
- float elapsed = System.currentTimeMillis() - mAnimateTargetTime;
- float p = mMotionEasing.get(elapsed / mAnimateDuration);
- mValue = (1 - p) * mStartRotationX + p * mTargetRotationX;
- if (p >= 1f) {
- mAnimate = false;
+ if (value != mValue) {
+ long lastUpdate = System.currentTimeMillis();
+ long interval = lastUpdate - mLastUpdate;
+ if (interval > mAnimateDuration && mLastUpdate != 0L) {
+ mAnimateValueChanges = true;
+ } else {
+ mAnimateValueChanges = false;
}
+ mLastUpdate = lastUpdate;
+ }
+ if (!mAnimateValueChanges) {
+ mValue = value;
} else {
- mValue = mTargetRotationX;
+ if (value != mValue && !mAnimate) {
+ // animate
+ mStartRotationX = mValue;
+ mTargetRotationX = value;
+ mAnimate = true;
+ mAnimateTargetTime = System.currentTimeMillis();
+ mMotionEasing =
+ new FloatAnimation(
+ mMotionEasingType, mAnimateDuration / 1000f, null, 0f, Float.NaN);
+ mMotionEasing.setTargetValue(1f);
+ }
+ if (mAnimate) {
+ float elapsed = System.currentTimeMillis() - mAnimateTargetTime;
+ float p = mMotionEasing.get(elapsed / mAnimateDuration);
+ mValue = (1 - p) * mStartRotationX + p * mTargetRotationX;
+ if (p >= 1f) {
+ mAnimate = false;
+ }
+ } else {
+ mValue = mTargetRotationX;
+ }
}
-
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 34b7a2326a21..511858aa2b29 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
@@ -28,7 +28,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
/** Represents the content of a CanvasLayout (i.e. contains the canvas commands) */
-public class CanvasContent extends Component implements ComponentStartOperation {
+public class CanvasContent extends Component {
public CanvasContent(
int componentId,
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 e05bdf2b824d..5f084e938588 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
@@ -42,7 +42,11 @@ import java.util.List;
/** Represents a click modifier + actions */
public class ClickModifierOperation extends PaintOperation
- implements ModifierOperation, DecoratorComponent, ClickHandler, AccessibleComponent {
+ implements Container,
+ ModifierOperation,
+ DecoratorComponent,
+ ClickHandler,
+ AccessibleComponent {
private static final int OP_CODE = Operations.MODIFIER_CLICK;
long mAnimateRippleStart = 0;
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 8a77dc3aafa5..eee2aabe1d8b 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
@@ -41,7 +41,8 @@ import java.util.ArrayList;
import java.util.HashSet;
/** Generic Component class */
-public class Component extends PaintOperation implements Measurable, SerializableToString {
+public class Component extends PaintOperation
+ implements Container, Measurable, SerializableToString {
private static final boolean DEBUG = false;
@@ -68,9 +69,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
private boolean mNeedsBoundsAnimation = false;
- /**
- * Mark the component as needing a bounds animation pass
- */
+ /** Mark the component as needing a bounds animation pass */
public void markNeedsBoundsAnimation() {
mNeedsBoundsAnimation = true;
if (mParent != null && !mParent.mNeedsBoundsAnimation) {
@@ -78,9 +77,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
}
- /**
- * Clear the bounds animation pass flag
- */
+ /** Clear the bounds animation pass flag */
public void clearNeedsBoundsAnimation() {
mNeedsBoundsAnimation = false;
}
@@ -426,13 +423,13 @@ public class Component extends PaintOperation implements Measurable, Serializabl
/**
* Animate the bounds of the component as needed
+ *
* @param context
*/
public void animatingBounds(@NonNull RemoteContext context) {
if (mAnimateMeasure != null) {
mAnimateMeasure.apply(context);
updateComponentValues(context);
- markNeedsBoundsAnimation();
} else {
clearNeedsBoundsAnimation();
}
@@ -784,7 +781,13 @@ public class Component extends PaintOperation implements Measurable, Serializabl
public boolean applyAnimationAsNeeded(@NonNull PaintContext context) {
if (context.isAnimationEnabled() && mAnimateMeasure != null) {
mAnimateMeasure.paint(context);
- context.needsRepaint();
+ if (mAnimateMeasure.isDone()) {
+ mAnimateMeasure = null;
+ clearNeedsBoundsAnimation();
+ needsRepaint();
+ } else {
+ markNeedsBoundsAnimation();
+ }
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
deleted file mode 100644
index 5da06634d101..000000000000
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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;
-
-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 java.util.List;
-
-public class ComponentEnd extends Operation {
-
- @Override
- public void write(@NonNull WireBuffer buffer) {
- apply(buffer);
- }
-
- @NonNull
- @Override
- public String toString() {
- return "COMPONENT_END";
- }
-
- @Override
- public void apply(@NonNull RemoteContext context) {
- // nothing
- }
-
- @NonNull
- @Override
- public String deepToString(@NonNull String indent) {
- return (indent != null ? indent : "") + toString();
- }
-
- /**
- * The name of the class
- *
- * @return the name
- */
- @NonNull
- public static String name() {
- return "ComponentEnd";
- }
-
- /**
- * The OP_CODE for this command
- *
- * @return the opcode
- */
- public static int id() {
- return Operations.COMPONENT_END;
- }
-
- public static void apply(@NonNull WireBuffer buffer) {
- buffer.start(Operations.COMPONENT_END);
- }
-
- public static int size() {
- return 1 + 4 + 4 + 4;
- }
-
- /**
- * 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) {
- 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(
- "End tag for components / layouts. This operation marks the end"
- + "of a component");
- }
-}
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 4349b31d76e3..f009d8801159 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
@@ -26,9 +26,10 @@ 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 java.util.ArrayList;
import java.util.List;
-public class ComponentStart extends Operation implements ComponentStartOperation {
+public class ComponentStart extends Operation implements Container {
int mType = DEFAULT;
float mX;
@@ -37,6 +38,8 @@ public class ComponentStart extends Operation implements ComponentStartOperation
float mHeight;
int mComponentId;
+ @NonNull public ArrayList<Operation> mList = new ArrayList<>();
+
public int getType() {
return mType;
}
@@ -217,4 +220,10 @@ public class ComponentStart extends Operation implements ComponentStartOperation
.field(FLOAT, "WIDTH", "width of the component")
.field(FLOAT, "HEIGHT", "height of the component");
}
+
+ @NonNull
+ @Override
+ public ArrayList<Operation> getList() {
+ return mList;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Container.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Container.java
new file mode 100644
index 000000000000..c678f6c22cef
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Container.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+
+import java.util.ArrayList;
+
+public interface Container {
+ @NonNull
+ ArrayList<Operation> getList();
+}
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/ContainerEnd.java
index 12a673d7380f..4290c4bc3c2b 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/ContainerEnd.java
@@ -25,7 +25,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
-public class OperationsListEnd extends Operation {
+public class ContainerEnd extends Operation {
@Override
public void write(@NonNull WireBuffer buffer) {
@@ -65,7 +65,7 @@ public class OperationsListEnd extends Operation {
* @return the opcode
*/
public static int id() {
- return Operations.OPERATIONS_LIST_END;
+ return Operations.CONTAINER_END;
}
public static void apply(@NonNull WireBuffer buffer) {
@@ -79,7 +79,7 @@ public class OperationsListEnd extends Operation {
* @param operations the list of operations that will be added to
*/
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
- operations.add(new OperationsListEnd());
+ operations.add(new ContainerEnd());
}
/**
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 9bfbe6a42a37..27172aa7672c 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
@@ -28,7 +28,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
/** Represents the content of a LayoutComponent (i.e. the children components) */
-public class LayoutComponentContent extends Component implements ComponentStartOperation {
+public class LayoutComponentContent extends Component {
public LayoutComponentContent(
int componentId,
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 9fc5da8320ba..6dce6f115572 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
@@ -29,7 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.ArrayList;
public abstract class ListActionsOperation extends PaintOperation
- implements ModifierOperation, DecoratorComponent {
+ implements Container, ModifierOperation, DecoratorComponent {
String mOperationName;
protected float mWidth = 0;
@@ -43,6 +43,7 @@ public abstract class ListActionsOperation extends PaintOperation
public ArrayList<Operation> mList = new ArrayList<>();
+ @NonNull
public ArrayList<Operation> getList() {
return mList;
}
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
deleted file mode 100644
index 3d389e5badef..000000000000
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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;
-
-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 java.util.List;
-
-public class LoopEnd extends Operation {
-
- @Override
- public void write(@NonNull WireBuffer buffer) {
- apply(buffer);
- }
-
- @NonNull
- @Override
- public String toString() {
- return "LOOP_END";
- }
-
- @Override
- public void apply(@NonNull RemoteContext context) {
- // nothing
- }
-
- @NonNull
- @Override
- public String deepToString(@NonNull String indent) {
- return (indent != null ? indent : "") + toString();
- }
-
- /**
- * The name of the class
- *
- * @return the name
- */
- @NonNull
- public static String name() {
- return "LoopEnd";
- }
-
- /**
- * The OP_CODE for this command
- *
- * @return the opcode
- */
- public static int id() {
- return Operations.LOOP_END;
- }
-
- public static void apply(@NonNull WireBuffer buffer) {
- buffer.start(id());
- }
-
- /**
- * 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) {
- 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 ab1e0ac73368..f5954eea04af 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
@@ -32,7 +32,7 @@ import java.util.ArrayList;
import java.util.List;
/** Represents a loop of operations */
-public class LoopOperation extends PaintOperation implements VariableSupport {
+public class LoopOperation extends PaintOperation implements Container, VariableSupport {
private static final int OP_CODE = Operations.LOOP_START;
@NonNull public ArrayList<Operation> mList = new ArrayList<>();
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 11fa7ee670dd..baff5ee488a7 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
@@ -36,7 +36,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
/** Represents the root layout component. Entry point to the component tree layout/paint. */
-public class RootLayoutComponent extends Component implements ComponentStartOperation {
+public class RootLayoutComponent extends Component {
private int mCurrentId = -1;
private boolean mHasTouchListeners = false;
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 1de956b7e5d7..d3b3e0e775f2 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
@@ -323,7 +323,6 @@ public class AnimateMeasure {
}
if (mP >= 1f && mVp >= 1f) {
- mComponent.mAnimateMeasure = null;
mComponent.mVisibility = mTarget.getVisibility();
}
}
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 8076cb10ea0c..a37f35f0c8d8 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
@@ -26,7 +26,6 @@ import com.android.internal.widget.remotecompose.core.PaintContext;
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.ComponentStartOperation;
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;
@@ -34,7 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import java.util.List;
/** Simple Box layout implementation */
-public class BoxLayout extends LayoutManager implements ComponentStartOperation {
+public class BoxLayout extends LayoutManager {
public static final int START = 1;
public static final int CENTER = 2;
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 249e84a1c1bc..f68d7b439578 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
@@ -28,7 +28,6 @@ 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.ComponentStartOperation;
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;
@@ -40,7 +39,7 @@ import java.util.List;
/**
* Simple Column layout implementation - also supports weight and horizontal/vertical positioning
*/
-public class ColumnLayout extends LayoutManager implements ComponentStartOperation {
+public class ColumnLayout extends LayoutManager {
public static final int START = 1;
public static final int CENTER = 2;
public static final int END = 3;
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 a5edaa8de3af..edfd69cbfa96 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
@@ -135,33 +135,26 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
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(context.getContext());
+ maxWidth = intrinsicWidth(context.getContext()) + mPaddingLeft + mPaddingRight;
}
if (mHeightModifier.isIntrinsicMin()) {
- maxHeight = intrinsicHeight(context.getContext());
+ maxHeight = intrinsicHeight(context.getContext()) + mPaddingTop + mPaddingBottom;
}
+ float insetMaxWidth = maxWidth - mPaddingLeft - mPaddingRight;
+ float insetMaxHeight = maxHeight - mPaddingTop - mPaddingBottom;
+
boolean hasHorizontalWrap = mWidthModifier.isWrap();
boolean hasVerticalWrap = mHeightModifier.isWrap();
if (hasHorizontalWrap || hasVerticalWrap) { // TODO: potential npe -- bbade@
mCachedWrapSize.setWidth(0f);
mCachedWrapSize.setHeight(0f);
- float wrapMaxWidth = insetMaxWidth;
- float wrapMaxHeight = insetMaxHeight;
- if (hasHorizontalWrap) {
- wrapMaxWidth = insetMaxWidth - mPaddingLeft - mPaddingRight;
- }
- if (hasVerticalWrap) {
- wrapMaxHeight = insetMaxHeight - mPaddingTop - mPaddingBottom;
- }
computeWrapSize(
context,
- wrapMaxWidth,
- wrapMaxHeight,
+ insetMaxWidth,
+ insetMaxHeight,
mWidthModifier.isWrap(),
mHeightModifier.isWrap(),
measure,
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 37b9a688af8b..b688f6e4175a 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
@@ -28,7 +28,6 @@ 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.ComponentStartOperation;
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;
@@ -38,7 +37,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.utils.De
import java.util.List;
/** Simple Row layout implementation - also supports weight and horizontal/vertical positioning */
-public class RowLayout extends LayoutManager implements ComponentStartOperation {
+public class RowLayout extends LayoutManager {
public static final int START = 1;
public static final int CENTER = 2;
public static final int END = 3;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
index 61a3ec964142..3044797b17c9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -26,7 +26,6 @@ import com.android.internal.widget.remotecompose.core.PaintOperation;
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.ComponentStartOperation;
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;
@@ -43,7 +42,7 @@ import java.util.Map;
* <p>States are defined as child layouts. This layout handles interpolating between the different
* state in order to provide an automatic transition.
*/
-public class StateLayout extends LayoutManager implements ComponentStartOperation {
+public class StateLayout extends LayoutManager {
public int measuredLayoutIndex = 0;
public int currentLayoutIndex = 0;
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 910205e8a7e2..8157ea05ec45 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
@@ -29,7 +29,6 @@ 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.ComponentStartOperation;
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.paint.PaintBundle;
@@ -39,8 +38,7 @@ import com.android.internal.widget.remotecompose.core.semantics.AccessibleCompon
import java.util.List;
/** Text component, referencing a text id */
-public class TextLayout extends LayoutManager
- implements ComponentStartOperation, VariableSupport, AccessibleComponent {
+public class TextLayout extends LayoutManager implements VariableSupport, AccessibleComponent {
private static final boolean DEBUG = false;
private int mTextId = -1;
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 a5f79ee7e7b7..8950579354b7 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
@@ -31,6 +31,7 @@ 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.LayoutComponent;
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;
@@ -212,17 +213,38 @@ public class ScrollModifierOperation extends ListActionsOperation
.field(INT, "direction", "");
}
+ private float getMaxScrollPosition(Component component, int direction) {
+ if (component instanceof LayoutComponent) {
+ LayoutComponent layoutComponent = (LayoutComponent) component;
+ int numChildren = layoutComponent.getChildrenComponents().size();
+ if (numChildren > 0) {
+ Component lastChild = layoutComponent.getChildrenComponents().get(numChildren - 1);
+ if (direction == 0) { // VERTICAL
+ return lastChild.getY();
+ } else {
+ return lastChild.getX();
+ }
+ }
+ }
+ return 0f;
+ }
+
@Override
public void layout(RemoteContext context, Component component, float width, float height) {
mWidth = width;
mHeight = height;
- if (mDirection == 0) { // VERTICAL
- context.loadFloat(Utils.idFromNan(mMax), mMaxScrollY);
- context.loadFloat(Utils.idFromNan(mNotchMax), mContentDimension);
- } else {
- context.loadFloat(Utils.idFromNan(mMax), mMaxScrollX);
- context.loadFloat(Utils.idFromNan(mNotchMax), mContentDimension);
+ float max = mMaxScrollY;
+ if (mDirection != 0) { // HORIZONTAL
+ max = mMaxScrollX;
+ }
+ if (mTouchExpression != null) {
+ float maxScrollPosition = getMaxScrollPosition(component, mDirection);
+ if (maxScrollPosition > 0) {
+ max = maxScrollPosition;
+ }
}
+ context.loadFloat(Utils.idFromNan(mMax), max);
+ context.loadFloat(Utils.idFromNan(mNotchMax), mContentDimension);
}
@Override
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 6eb83f1da410..5de11a19799d 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.player;
+import static com.android.internal.widget.remotecompose.core.CoreDocument.MAJOR_VERSION;
+import static com.android.internal.widget.remotecompose.core.CoreDocument.MINOR_VERSION;
+
import android.app.Application;
import android.content.Context;
import android.content.res.TypedArray;
@@ -32,6 +35,7 @@ import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.remotecompose.accessibility.RemoteComposeTouchHelper;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -43,8 +47,8 @@ import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCa
public class RemoteComposePlayer extends FrameLayout {
private RemoteComposeCanvas mInner;
- private static final int MAX_SUPPORTED_MAJOR_VERSION = 0;
- private static final int MAX_SUPPORTED_MINOR_VERSION = 1;
+ private static final int MAX_SUPPORTED_MAJOR_VERSION = MAJOR_VERSION;
+ private static final int MAX_SUPPORTED_MINOR_VERSION = MINOR_VERSION;
public RemoteComposePlayer(Context context) {
super(context);
@@ -259,6 +263,16 @@ public class RemoteComposePlayer extends FrameLayout {
return mInner.getDocument().mDocument.getOpsPerFrame();
}
+ /**
+ * Set to use the choreographer
+ *
+ * @param value
+ */
+ @VisibleForTesting
+ public void setUseChoreographer(boolean value) {
+ mInner.setUseChoreographer(value);
+ }
+
/** Id action callback interface */
public interface IdActionCallbacks {
/**
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 0712ea496b57..16e0e054ea4f 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
@@ -16,6 +16,7 @@
package com.android.internal.widget.remotecompose.player.platform;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Canvas;
@@ -247,6 +248,8 @@ public class AndroidPaintContext extends PaintContext {
mCanvas.drawTextOnPath(getText(textId), getPath(pathId, 0, 1), hOffset, vOffset, mPaint);
}
+ private Paint.FontMetrics mCachedFontMetrics;
+
@Override
public void getTextBounds(int textId, int start, int end, int flags, @NonNull float[] bounds) {
String str = getText(textId);
@@ -254,7 +257,10 @@ public class AndroidPaintContext extends PaintContext {
end = str.length();
}
- Paint.FontMetrics metrics = mPaint.getFontMetrics();
+ if (mCachedFontMetrics == null) {
+ mCachedFontMetrics = mPaint.getFontMetrics();
+ }
+ mPaint.getFontMetrics(mCachedFontMetrics);
mPaint.getTextBounds(str, start, end, mTmpRect);
bounds[0] = mTmpRect.left;
@@ -266,8 +272,8 @@ public class AndroidPaintContext extends PaintContext {
}
if ((flags & PaintContext.TEXT_MEASURE_FONT_HEIGHT) != 0) {
- bounds[1] = Math.round(metrics.ascent);
- bounds[3] = Math.round(metrics.bottom);
+ bounds[1] = Math.round(mCachedFontMetrics.ascent);
+ bounds[3] = Math.round(mCachedFontMetrics.bottom);
} else {
bounds[1] = mTmpRect.top;
bounds[3] = mTmpRect.bottom;
@@ -415,225 +421,218 @@ public class AndroidPaintContext extends PaintContext {
return null;
}
- /**
- * This applies paint changes to the current paint
- *
- * @param paintData the list change to the paint
- */
- @Override
- public void applyPaint(@NonNull PaintBundle paintData) {
- paintData.applyPaintChange(
- (PaintContext) this,
- new PaintChanges() {
- @Override
- public void setTextSize(float size) {
- mPaint.setTextSize(size);
- }
-
- @Override
- public void setTypeFace(int fontType, int weight, boolean italic) {
- int[] type =
- new int[] {
- Typeface.NORMAL,
- Typeface.BOLD,
- Typeface.ITALIC,
- Typeface.BOLD_ITALIC
- };
-
- switch (fontType) {
- case PaintBundle.FONT_TYPE_DEFAULT:
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.DEFAULT);
- } else {
- mPaint.setTypeface(
- Typeface.create(Typeface.DEFAULT, weight, italic));
- }
- break;
- case PaintBundle.FONT_TYPE_SERIF:
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.SERIF);
- } else {
- mPaint.setTypeface(
- Typeface.create(Typeface.SERIF, weight, italic));
- }
- break;
- case PaintBundle.FONT_TYPE_SANS_SERIF:
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.SANS_SERIF);
- } else {
- mPaint.setTypeface(
- Typeface.create(Typeface.SANS_SERIF, weight, italic));
- }
- break;
- case PaintBundle.FONT_TYPE_MONOSPACE:
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.MONOSPACE);
- } else {
- mPaint.setTypeface(
- Typeface.create(Typeface.MONOSPACE, weight, italic));
- }
-
- break;
- }
- }
-
- @Override
- public void setStrokeWidth(float width) {
- mPaint.setStrokeWidth(width);
- }
-
- @Override
- public void setColor(int color) {
- mPaint.setColor(color);
- }
-
- @Override
- public void setStrokeCap(int cap) {
- mPaint.setStrokeCap(Paint.Cap.values()[cap]);
- }
-
- @Override
- public void setStyle(int style) {
- mPaint.setStyle(Paint.Style.values()[style]);
- }
-
- @Override
- public void setShader(int shaderId) {
- // TODO this stuff should check the shader creation
- if (shaderId == 0) {
- mPaint.setShader(null);
- return;
- }
- ShaderData data = getShaderData(shaderId);
- if (data == null) {
- return;
- }
- RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
- String[] names = data.getUniformFloatNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- float[] val = data.getUniformFloats(name);
- shader.setFloatUniform(name, val);
- }
- names = data.getUniformIntegerNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- int[] val = data.getUniformInts(name);
- shader.setIntUniform(name, val);
- }
- names = data.getUniformBitmapNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- int val = data.getUniformBitmapId(name);
- }
- mPaint.setShader(shader);
- }
-
- @Override
- public void setImageFilterQuality(int quality) {
- Utils.log(" quality =" + quality);
- mPaint.setFilterBitmap(quality == 1);
- }
-
- @Override
- public void setBlendMode(int mode) {
- mPaint.setBlendMode(origamiToBlendMode(mode));
- }
+ PaintChanges mCachedPaintChanges =
+ new PaintChanges() {
+ @Override
+ public void setTextSize(float size) {
+ mPaint.setTextSize(size);
+ }
+
+ @Override
+ public void setTypeFace(int fontType, int weight, boolean italic) {
+ int[] type =
+ new int[] {
+ Typeface.NORMAL,
+ Typeface.BOLD,
+ Typeface.ITALIC,
+ Typeface.BOLD_ITALIC
+ };
- @Override
- public void setAlpha(float a) {
- mPaint.setAlpha((int) (255 * a));
+ switch (fontType) {
+ case PaintBundle.FONT_TYPE_DEFAULT:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.DEFAULT);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.DEFAULT, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_SERIF:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.SERIF);
+ } else {
+ mPaint.setTypeface(Typeface.create(Typeface.SERIF, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_SANS_SERIF:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.SANS_SERIF);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.SANS_SERIF, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_MONOSPACE:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.MONOSPACE);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.MONOSPACE, weight, italic));
+ }
+
+ break;
}
-
- @Override
- public void setStrokeMiter(float miter) {
- mPaint.setStrokeMiter(miter);
+ }
+
+ @Override
+ public void setStrokeWidth(float width) {
+ mPaint.setStrokeWidth(width);
+ }
+
+ @Override
+ public void setColor(int color) {
+ mPaint.setColor(color);
+ }
+
+ @Override
+ public void setStrokeCap(int cap) {
+ mPaint.setStrokeCap(Paint.Cap.values()[cap]);
+ }
+
+ @Override
+ public void setStyle(int style) {
+ mPaint.setStyle(Paint.Style.values()[style]);
+ }
+
+ @Override
+ public void setShader(int shaderId) {
+ // TODO this stuff should check the shader creation
+ if (shaderId == 0) {
+ mPaint.setShader(null);
+ return;
}
-
- @Override
- public void setStrokeJoin(int join) {
- mPaint.setStrokeJoin(Paint.Join.values()[join]);
+ ShaderData data = getShaderData(shaderId);
+ if (data == null) {
+ return;
}
-
- @Override
- public void setFilterBitmap(boolean filter) {
- mPaint.setFilterBitmap(filter);
+ RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
+ String[] names = data.getUniformFloatNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ float[] val = data.getUniformFloats(name);
+ shader.setFloatUniform(name, val);
}
-
- @Override
- public void setAntiAlias(boolean aa) {
- mPaint.setAntiAlias(aa);
+ names = data.getUniformIntegerNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int[] val = data.getUniformInts(name);
+ shader.setIntUniform(name, val);
}
-
- @Override
- public void clear(long mask) {
- if ((mask & (1L << PaintBundle.COLOR_FILTER)) != 0) {
- mPaint.setColorFilter(null);
- }
+ names = data.getUniformBitmapNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int val = data.getUniformBitmapId(name);
}
-
- Shader.TileMode[] mTileModes =
- new Shader.TileMode[] {
- Shader.TileMode.CLAMP,
- Shader.TileMode.REPEAT,
- Shader.TileMode.MIRROR
- };
-
- @Override
- public void setLinearGradient(
- @NonNull int[] colors,
- @NonNull float[] stops,
- float startX,
- float startY,
- float endX,
- float endY,
- int tileMode) {
- mPaint.setShader(
- new LinearGradient(
- startX,
- startY,
- endX,
- endY,
- colors,
- stops,
- mTileModes[tileMode]));
+ mPaint.setShader(shader);
+ }
+
+ @Override
+ public void setImageFilterQuality(int quality) {
+ Utils.log(" quality =" + quality);
+ mPaint.setFilterBitmap(quality == 1);
+ }
+
+ @Override
+ public void setBlendMode(int mode) {
+ mPaint.setBlendMode(origamiToBlendMode(mode));
+ }
+
+ @Override
+ public void setAlpha(float a) {
+ mPaint.setAlpha((int) (255 * a));
+ }
+
+ @Override
+ public void setStrokeMiter(float miter) {
+ mPaint.setStrokeMiter(miter);
+ }
+
+ @Override
+ public void setStrokeJoin(int join) {
+ mPaint.setStrokeJoin(Paint.Join.values()[join]);
+ }
+
+ @Override
+ public void setFilterBitmap(boolean filter) {
+ mPaint.setFilterBitmap(filter);
+ }
+
+ @Override
+ public void setAntiAlias(boolean aa) {
+ mPaint.setAntiAlias(aa);
+ }
+
+ @Override
+ public void clear(long mask) {
+ if ((mask & (1L << PaintBundle.COLOR_FILTER)) != 0) {
+ mPaint.setColorFilter(null);
}
-
- @Override
- public void setRadialGradient(
- @NonNull int[] colors,
- @NonNull float[] stops,
- float centerX,
- float centerY,
- float radius,
- int tileMode) {
- mPaint.setShader(
- new RadialGradient(
- centerX,
- centerY,
- radius,
- colors,
- stops,
- mTileModes[tileMode]));
+ }
+
+ Shader.TileMode[] mTileModes =
+ new Shader.TileMode[] {
+ Shader.TileMode.CLAMP, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR
+ };
+
+ @Override
+ public void setLinearGradient(
+ @NonNull int[] colors,
+ @NonNull float[] stops,
+ float startX,
+ float startY,
+ float endX,
+ float endY,
+ int tileMode) {
+ mPaint.setShader(
+ new LinearGradient(
+ startX,
+ startY,
+ endX,
+ endY,
+ colors,
+ stops,
+ mTileModes[tileMode]));
+ }
+
+ @Override
+ public void setRadialGradient(
+ @NonNull int[] colors,
+ @NonNull float[] stops,
+ float centerX,
+ float centerY,
+ float radius,
+ int tileMode) {
+ mPaint.setShader(
+ new RadialGradient(
+ centerX, centerY, radius, colors, stops, mTileModes[tileMode]));
+ }
+
+ @Override
+ public void setSweepGradient(
+ @NonNull int[] colors,
+ @NonNull float[] stops,
+ float centerX,
+ float centerY) {
+ mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
+ }
+
+ @Override
+ public void setColorFilter(int color, int mode) {
+ PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
+ if (pmode != null) {
+ mPaint.setColorFilter(new PorterDuffColorFilter(color, pmode));
}
+ }
+ };
- @Override
- public void setSweepGradient(
- @NonNull int[] colors,
- @NonNull float[] stops,
- float centerX,
- float centerY) {
- mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
- }
-
- @Override
- public void setColorFilter(int color, int mode) {
- PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
- if (pmode != null) {
- mPaint.setColorFilter(new PorterDuffColorFilter(color, pmode));
- }
- }
- });
+ /**
+ * This applies paint changes to the current paint
+ *
+ * @param paintData the list change to the paint
+ */
+ @Override
+ public void applyPaint(@NonNull PaintBundle paintData) {
+ paintData.applyPaintChange(this, mCachedPaintChanges);
}
@Override
@@ -774,7 +773,8 @@ public class AndroidPaintContext extends PaintContext {
return path;
}
- private String getText(int id) {
+ @Override
+ public @Nullable String getText(int id) {
return (String) mContext.mRemoteComposeState.getFromId(id);
}
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 0fb0a28da1db..9d385ddafe19 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
@@ -21,6 +21,7 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+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;
@@ -40,7 +41,8 @@ import java.util.HashMap;
*
* <p>This is used to play the RemoteCompose operations on Android.
*/
-class AndroidRemoteContext extends RemoteContext {
+@VisibleForTesting
+public class AndroidRemoteContext extends RemoteContext {
public void useCanvas(Canvas canvas) {
if (mPaintContext == null) {
@@ -121,6 +123,40 @@ class AndroidRemoteContext extends RemoteContext {
mVarNameHashMap.put(integerName, null);
}
+ @Override
+ public void setNamedFloatOverride(String floatName, float value) {
+ if (mVarNameHashMap.get(floatName) != null) {
+ int id = mVarNameHashMap.get(floatName).mId;
+ overrideFloat(id, value);
+ }
+ }
+
+ @Override
+ public void clearNamedFloatOverride(String floatName) {
+ if (mVarNameHashMap.get(floatName) != null) {
+ int id = mVarNameHashMap.get(floatName).mId;
+ clearFloatOverride(id);
+ }
+ mVarNameHashMap.put(floatName, null);
+ }
+
+ @Override
+ public void setNamedDataOverride(String dataName, Object value) {
+ if (mVarNameHashMap.get(dataName) != null) {
+ int id = mVarNameHashMap.get(dataName).mId;
+ overrideData(id, value);
+ }
+ }
+
+ @Override
+ public void clearNamedDataOverride(String dataName) {
+ if (mVarNameHashMap.get(dataName) != null) {
+ int id = mVarNameHashMap.get(dataName).mId;
+ clearDataOverride(id);
+ }
+ mVarNameHashMap.put(dataName, null);
+ }
+
/**
* Override a color to force it to be the color provided
*
@@ -236,6 +272,10 @@ class AndroidRemoteContext extends RemoteContext {
mRemoteComposeState.overrideInteger(id, value);
}
+ public void overrideData(int id, Object value) {
+ mRemoteComposeState.overrideData(id, value);
+ }
+
public void clearDataOverride(int id) {
mRemoteComposeState.clearDataOverride(id);
}
@@ -244,6 +284,10 @@ class AndroidRemoteContext extends RemoteContext {
mRemoteComposeState.clearIntegerOverride(id);
}
+ public void clearFloatOverride(int id) {
+ mRemoteComposeState.clearFloatOverride(id);
+ }
+
@Override
public String getText(int id) {
return (String) mRemoteComposeState.getFromId(id);
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 c7b1166e113e..da65a9cf5cc9 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
@@ -16,10 +16,12 @@
package com.android.internal.widget.remotecompose.player.platform;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.util.AttributeSet;
+import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -44,6 +46,22 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
boolean mHasClickAreas = false;
Point mActionDownPoint = new Point(0, 0);
AndroidRemoteContext mARContext = new AndroidRemoteContext();
+ float mDensity = 1f;
+
+ long mLastFrameDelay = 1;
+ float mMaxFrameRate = 60f; // frames per seconds
+ long mMaxFrameDelay = (long) (1000 / mMaxFrameRate);
+
+ private Choreographer mChoreographer;
+ private Choreographer.FrameCallback mFrameCallback =
+ new Choreographer.FrameCallback() {
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ mARContext.currentTime = frameTimeNanos / 1000000;
+ mARContext.setDebug(mDebug);
+ postInvalidateOnAnimation();
+ }
+ };
public RemoteComposeCanvas(Context context) {
super(context);
@@ -85,6 +103,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
public void setDocument(RemoteComposeDocument value) {
mDocument = value;
mDocument.initializeContext(mARContext);
+ mARContext.setAnimationEnabled(true);
+ mARContext.setDensity(mDensity);
+ mARContext.setUseChoreographer(true);
setContentDescription(mDocument.getDocument().getContentDescription());
updateClickAreas();
requestLayout();
@@ -93,6 +114,11 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
@Override
public void onViewAttachedToWindow(View view) {
+ if (mChoreographer == null) {
+ mChoreographer = Choreographer.getInstance();
+ mChoreographer.postFrameCallback(mFrameCallback);
+ }
+ mDensity = getContext().getResources().getDisplayMetrics().density;
if (mDocument == null) {
return;
}
@@ -136,6 +162,10 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
@Override
public void onViewDetachedFromWindow(View view) {
+ if (mChoreographer != null) {
+ mChoreographer.removeFrameCallback(mFrameCallback);
+ mChoreographer = null;
+ }
removeAllViews();
}
@@ -195,6 +225,34 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
}
}
+ public void setLocalFloat(String name, Float content) {
+ mARContext.setNamedFloatOverride(name, content);
+ if (mDocument != null) {
+ mDocument.invalidate();
+ }
+ }
+
+ public void clearLocalFloat(String name) {
+ mARContext.clearNamedFloatOverride(name);
+ if (mDocument != null) {
+ mDocument.invalidate();
+ }
+ }
+
+ public void setLocalBitmap(String name, Bitmap content) {
+ mARContext.setNamedDataOverride(name, content);
+ if (mDocument != null) {
+ mDocument.invalidate();
+ }
+ }
+
+ public void clearLocalBitmap(String name) {
+ mARContext.clearNamedDataOverride(name);
+ if (mDocument != null) {
+ mDocument.invalidate();
+ }
+ }
+
public int hasSensorListeners(int[] ids) {
int count = 0;
for (int id = RemoteContext.ID_ACCELERATION_X; id <= RemoteContext.ID_LIGHT; id++) {
@@ -236,6 +294,15 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mDocument.getDocument().checkShaders(mARContext, shaderControl);
}
+ /**
+ * Set to true to use the choreographer
+ *
+ * @param value
+ */
+ public void setUseChoreographer(boolean value) {
+ mARContext.setUseChoreographer(value);
+ }
+
public interface ClickCallbacks {
void click(int id, String metadata);
}
@@ -414,12 +481,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
return;
}
long start = mEvalTime ? System.nanoTime() : 0;
- mARContext.setAnimationEnabled(true);
- mARContext.currentTime = System.currentTimeMillis();
- mARContext.setDebug(mDebug);
- float density = getContext().getResources().getDisplayMetrics().density;
mARContext.useCanvas(canvas);
- mARContext.setDensity(density);
mARContext.mWidth = getWidth();
mARContext.mHeight = getHeight();
mDocument.paint(mARContext, mTheme);
@@ -431,8 +493,19 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mTime = System.nanoTime();
}
}
- if (mDocument.needsRepaint() > 0) {
- invalidate();
+ int nextFrame = mDocument.needsRepaint();
+ if (nextFrame > 0) {
+ mLastFrameDelay = Math.max(mMaxFrameDelay, nextFrame);
+ if (mChoreographer != null) {
+ mChoreographer.postFrameCallbackDelayed(mFrameCallback, mLastFrameDelay);
+ }
+ if (!mARContext.useChoreographer()) {
+ invalidate();
+ }
+ } else {
+ if (mChoreographer != null) {
+ mChoreographer.removeFrameCallback(mFrameCallback);
+ }
}
if (mEvalTime) {
mDuration += System.nanoTime() - start;