diff options
47 files changed, 1110 insertions, 114 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 dea1caf76a5c..ca355c41f7a9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java @@ -35,6 +35,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LoopOper import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers; import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation; +import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap; 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; @@ -64,7 +65,7 @@ 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.0f; + static final float BUILD = 0.1f; @NonNull ArrayList<Operation> mOperations = new ArrayList<>(); @@ -99,6 +100,8 @@ public class CoreDocument implements Serializable { private int mLastId = 1; // last component id when inflating the file + private IntMap<Object> mDocProperties; + /** Returns a version number that is monotonically increasing. */ public static int getDocumentApiLevel() { return DOCUMENT_API_LEVEL; @@ -407,10 +410,31 @@ public class CoreDocument implements Serializable { @Override public void serialize(MapSerializer serializer) { - serializer.add("type", "CoreDocument"); - serializer.add("width", mWidth); - serializer.add("height", mHeight); - serializer.add("operations", mOperations); + serializer + .add("type", "CoreDocument") + .add("width", mWidth) + .add("height", mHeight) + .add("operations", mOperations); + } + + /** + * Set the properties of the document + * + * @param properties the properties to set + */ + public void setProperties(IntMap<Object> properties) { + mDocProperties = properties; + } + + /** + * @param key the key + * @return the value associated with the key + */ + public Object getProperty(short key) { + if (mDocProperties == null) { + return null; + } + return mDocProperties.get(key); } // ============== Haptic support ================== 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 a5b669d6aecf..1b0b9d783dff 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java @@ -207,6 +207,13 @@ public abstract class PaintContext { public abstract void restorePaint(); /** + * Replace the current paint with the PaintBundle + * + * @param paint + */ + public abstract void replacePaint(PaintBundle paint); + + /** * draw a round rect * * @param left left coordinate of the rectangle 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 c6ef1d3c457d..39f85f600310 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java @@ -191,6 +191,11 @@ public class RemoteComposeBuffer { // Supported operations on the buffer /////////////////////////////////////////////////////////////////////////////////////////////// + /** Insert a header */ + public void addHeader(short[] tags, Object[] values) { + Header.apply(mBuffer, tags, values); + } + /** * Insert a header * @@ -219,6 +224,28 @@ public class RemoteComposeBuffer { * @param width the width of the document in pixels * @param height the height of the document in pixels * @param contentDescription content description of the document + * @param capabilities bitmask indicating needed capabilities (unused for now) + */ + public void addHeader( + int width, + int height, + @Nullable String contentDescription, + float density, + long capabilities) { + Header.apply(mBuffer, width, height, density, capabilities); + int contentDescriptionId = 0; + if (contentDescription != null) { + contentDescriptionId = addText(contentDescription); + RootContentDescription.apply(mBuffer, contentDescriptionId); + } + } + + /** + * Insert a header + * + * @param width the width of the document in pixels + * @param height the height of the document in pixels + * @param contentDescription content description of the document */ public void header(int width, int height, @Nullable String contentDescription) { header(width, height, contentDescription, 1f, 0); 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 36e4ec1ff303..622f0c8d12b7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java @@ -26,6 +26,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess; import com.android.internal.widget.remotecompose.core.operations.utilities.CollectionsAccess; import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap; +import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap; import java.time.LocalDateTime; import java.time.OffsetDateTime; @@ -392,6 +393,7 @@ public abstract class RemoteContext { * @param width original width of the document when created * @param height original height of the document when created * @param capabilities bitmask of capabilities used in the document (TBD) + * @param properties properties of the document (TBD) */ public void header( int majorVersion, @@ -399,13 +401,15 @@ public abstract class RemoteContext { int patchVersion, int width, int height, - long capabilities) { + long capabilities, + IntMap<Object> properties) { mRemoteComposeState.setWindowWidth(width); mRemoteComposeState.setWindowHeight(height); mDocument.setVersion(majorVersion, minorVersion, patchVersion); mDocument.setWidth(width); mDocument.setHeight(height); mDocument.setRequiredCapabilities(capabilities); + mDocument.setProperties(properties); } /** diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java index a593241888df..d5af7914607a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java @@ -27,6 +27,8 @@ 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.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; @@ -34,7 +36,7 @@ import java.util.List; * Operation to Colors Color modes mMode = 0 two colors and a tween mMode = 1 color1 is a colorID. * mMode = 2 color2 is a colorID. mMode = 3 color1 & color2 are ids mMode = 4 H S V mode */ -public class ColorExpression extends Operation implements VariableSupport { +public class ColorExpression extends Operation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.COLOR_EXPRESSIONS; private static final String CLASS_NAME = "ColorExpression"; public int mId; @@ -502,4 +504,36 @@ public class ColorExpression extends Operation implements VariableSupport { public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("id", mId); + switch (mMode) { + case COLOR_COLOR_INTERPOLATE: + case ID_COLOR_INTERPOLATE: + case COLOR_ID_INTERPOLATE: + case ID_ID_INTERPOLATE: + serializer.add("mode", "TWEEN"); + serializer.add("startColor", mColor1, mOutColor1); + serializer.add("endColor", mColor2, mOutColor2); + serializer.add("startColor", mTween, mOutTween); + break; + case HSV_MODE: + serializer.add("mode", "HSV"); + serializer.add("hue", mHue, mOutHue); + serializer.add("sat", mSat, mOutSat); + serializer.add("val", mValue, mOutValue); + break; + case ARGB_MODE: + case IDARGB_MODE: + serializer.add("mode", "ARGB"); + serializer.add("a", mArgbAlpha, mOutArgbAlpha); + serializer.add("r", mArgbRed, mOutArgbRed); + serializer.add("g", mArgbGreen, mOutArgbGreen); + serializer.add("b", mArgbBlue, mOutArgbBlue); + break; + default: + serializer.add("mode", "NONE"); + } + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java index 7a72b109b2a8..fb3abdbb0626 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java @@ -28,11 +28,13 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.Arrays; import java.util.List; -public class DataListFloat extends Operation implements VariableSupport, ArrayAccess { +public class DataListFloat extends Operation implements VariableSupport, ArrayAccess, Serializable { private static final int OP_CODE = Operations.FLOAT_LIST; private static final String CLASS_NAME = "IdListData"; private final int mId; @@ -145,4 +147,9 @@ public class DataListFloat extends Operation implements VariableSupport, ArrayAc public int getLength() { return mValues.length; } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("id", mId).add("values", List.of(mValues)); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java index 7e29620ec104..58fd74fdcf3e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java @@ -29,11 +29,13 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.Arrays; import java.util.List; -public class DataListIds extends Operation implements VariableSupport, ArrayAccess { +public class DataListIds extends Operation implements VariableSupport, ArrayAccess, Serializable { private static final int OP_CODE = Operations.ID_LIST; private static final String CLASS_NAME = "IdListData"; private final int mId; @@ -147,4 +149,9 @@ public class DataListIds extends Operation implements VariableSupport, ArrayAcce public int getIntValue(int index) { return 0; } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("id", mId).add("ids", List.of(mIds)); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java index 6df4b91cc405..5dbaf29b8cc0 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java @@ -121,6 +121,6 @@ public abstract class DrawBase2 extends PaintOperation implements VariableSuppor } protected MapSerializer serialize(MapSerializer serializer, String v1Name, String v2Name) { - return serializer.add(v1Name, mV1, mValue1).add(v2Name, mV2, mValue2); + return serializer.add(v1Name, mValue1, mV1).add(v2Name, mValue2, mV2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java index 16ead454d84f..238f10e794d0 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java @@ -126,8 +126,8 @@ public abstract class DrawBase3 extends PaintOperation implements VariableSuppor protected MapSerializer serialize( MapSerializer serializer, String v1Name, String v2Name, String v3Name) { return serializer - .add(v1Name, mV1, mValue1) - .add(v2Name, mV2, mValue2) - .add(v3Name, mV3, mValue3); + .add(v1Name, mValue1, mV1) + .add(v2Name, mValue2, mV2) + .add(v3Name, mValue3, mV3); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java index 0733b833be11..ca34e01f1725 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java @@ -158,9 +158,9 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor protected MapSerializer serialize( MapSerializer serializer, String x1Name, String y1Name, String x2Name, String y2Name) { return serializer - .add(x1Name, mX1, mX1Value) - .add(y1Name, mY1, mY1Value) - .add(x2Name, mX2, mX2Value) - .add(y2Name, mY2, mY2Value); + .add(x1Name, mX1Value, mX1) + .add(y1Name, mY1Value, mY1) + .add(x2Name, mX2Value, mX2) + .add(y2Name, mY2Value, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java index 75b87c04e810..ca0584d0ee73 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java @@ -173,11 +173,11 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor String v5Name, String v6Name) { return serializer - .add(v1Name, mV1, mValue1) - .add(v2Name, mV2, mValue2) - .add(v3Name, mV3, mValue3) - .add(v4Name, mV4, mValue4) - .add(v5Name, mV5, mValue5) - .add(v6Name, mV6, mValue6); + .add(v1Name, mValue1, mV1) + .add(v2Name, mValue2, mV2) + .add(v3Name, mValue3, mV3) + .add(v4Name, mValue4, mV4) + .add(v5Name, mValue5, mV5) + .add(v6Name, mValue6, mV6); } } 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 5d0c43723ea1..92469d1834ba 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 @@ -26,11 +26,13 @@ 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.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Draw Text in Anchored to a point */ -public class DrawTextAnchored extends PaintOperation implements VariableSupport { +public class DrawTextAnchored extends PaintOperation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.DRAW_TEXT_ANCHOR; private static final String CLASS_NAME = "DrawTextAnchored"; int mTextID; @@ -238,4 +240,16 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport float y = Float.isNaN(mOutPanY) ? mOutY : mOutY + getVerticalOffset(); context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, (mFlags & ANCHOR_TEXT_RTL) == 1); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("textId", mTextID) + .add("x", mX, mOutX) + .add("y", mY, mOutY) + .add("panX", mPanX, mOutPanX) + .add("panY", mPanY, mOutPanY) + .add("flags", mFlags); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java index 3ab4a87c614c..1f7910ede4b5 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java @@ -26,11 +26,13 @@ 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.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Draw text along a path. */ -public class DrawTextOnPath extends PaintOperation implements VariableSupport { +public class DrawTextOnPath extends PaintOperation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.DRAW_TEXT_ON_PATH; private static final String CLASS_NAME = "DrawTextOnPath"; int mPathId; @@ -153,4 +155,14 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport { public void paint(@NonNull PaintContext context) { context.drawTextOnPath(mTextId, mPathId, mOutHOffset, mOutVOffset); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("pathId", mPathId) + .add("textId", mTextId) + .add("vOffset", mVOffset, mOutVOffset) + .add("hOffset", mHOffset, mOutHOffset); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java index e04e691c312c..7f3c3ed6bcff 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java @@ -25,30 +25,32 @@ 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.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; -/** Operation to deal with Text data */ -public class FloatConstant extends Operation { +/** Used to represent a float */ +public class FloatConstant extends Operation implements Serializable { private static final int OP_CODE = Operations.DATA_FLOAT; private static final String CLASS_NAME = "FloatConstant"; - public int mTextId; + public int mId; public float mValue; - public FloatConstant(int textId, float value) { - this.mTextId = textId; + public FloatConstant(int id, float value) { + this.mId = id; this.mValue = value; } @Override public void write(@NonNull WireBuffer buffer) { - apply(buffer, mTextId, mValue); + apply(buffer, mId, mValue); } @NonNull @Override public String toString() { - return "FloatConstant[" + mTextId + "] = " + mValue; + return "FloatConstant[" + mId + "] = " + mValue; } /** @@ -90,10 +92,10 @@ public class FloatConstant extends Operation { * @param operations the list of operations that will be added to */ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { - int textId = buffer.readInt(); + int id = buffer.readInt(); float value = buffer.readFloat(); - operations.add(new FloatConstant(textId, value)); + operations.add(new FloatConstant(id, value)); } /** @@ -110,7 +112,7 @@ public class FloatConstant extends Operation { @Override public void apply(@NonNull RemoteContext context) { - context.loadFloat(mTextId, mValue); + context.loadFloat(mId, mValue); } @NonNull @@ -118,4 +120,9 @@ public class FloatConstant extends Operation { public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("id", mId).add("value", mValue); + } } 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 e09745aa8546..c1fa898ec619 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 @@ -34,6 +34,9 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Anima import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap; import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; import com.android.internal.widget.remotecompose.core.operations.utilities.easing.SpringStopEngine; +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; @@ -42,7 +45,7 @@ import java.util.List; * like injecting the width of the component int draw rect As well as supporting generalized * animation floats. The floats represent a RPN style calculator */ -public class FloatExpression extends Operation implements VariableSupport { +public class FloatExpression extends Operation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.ANIMATED_FLOAT; private static final String CLASS_NAME = "FloatExpression"; public int mId; @@ -336,4 +339,14 @@ public class FloatExpression extends Operation implements VariableSupport { public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .addTags(SerializeTags.EXPRESSION) + .add("type", CLASS_NAME) + .add("id", mId) + .addFloatExpressionSrc("srcValues", mSrcValue) + .add("animation", mFloatAnimation); + } } 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 98c2745380d7..3d6316b67e8f 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 @@ -30,7 +30,11 @@ import com.android.internal.widget.remotecompose.core.RemoteComposeOperation; 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.utilities.IntMap; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.List; /** @@ -42,16 +46,75 @@ 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"; + private static final int MAGIC_NUMBER = 0x048C0000; // to uniquly identify the protocol int mMajorVersion; int mMinorVersion; int mPatchVersion; - int mWidth; - int mHeight; + int mWidth = 256; + int mHeight = 256; - float mDensity; - long mCapabilities; + float mDensity = 3; + long mCapabilities = 0; + private IntMap<Object> mProperties; + + /** + * Get a property on the header + * + * @param property the property to get + * @return the value of the property + */ + public Object get(short property) { + return mProperties.get(property); + } + + /** the width of the document */ + public static final short DOC_WIDTH = 5; + + /** The height of the document */ + public static final short DOC_HEIGHT = 6; + + /** The density at generation */ + public static final short DOC_DENSITY_AT_GENERATION = 7; + + /** The desired FPS for the document */ + public static final short DOC_DESIRED_FPS = 8; + + /** The description of the contents of the document */ + public static final short DOC_CONTENT_DESCRIPTION = 9; + + /** The source of the document */ + public static final short DOC_SOURCE = 11; + + /** The object is an integer */ + private static final short DATA_TYPE_INT = 0; + + /** The object is an float */ + private static final short DATA_TYPE_FLOAT = 1; + + /** The object is an LONG */ + private static final short DATA_TYPE_LONG = 2; + + /** The object is an UTF-8 encoded string */ + private static final short DATA_TYPE_STRING = 3; + + private static final short[] KEYS = { + DOC_WIDTH, + DOC_HEIGHT, + DOC_DENSITY_AT_GENERATION, + DOC_DESIRED_FPS, + DOC_CONTENT_DESCRIPTION, + DOC_SOURCE + }; + private static final String[] KEY_NAMES = { + "DOC_WIDTH", + "DOC_HEIGHT", + "DOC_DENSITY_AT_GENERATION", + "DOC_DESIRED_FPS", + "DOC_CONTENT_DESCRIPTION", + "DOC_SOURCE" + }; /** * It encodes the version of the document (following semantic versioning) as well as the @@ -82,6 +145,60 @@ public class Header extends Operation implements RemoteComposeOperation { this.mCapabilities = capabilities; } + /** + * @param majorVersion the major version of the RemoteCompose document API + * @param minorVersion the minor version of the RemoteCompose document API + * @param patchVersion the patch version of the RemoteCompose document API + * @param properties the properties of the document + */ + public Header(int majorVersion, int minorVersion, int patchVersion, IntMap<Object> properties) { + this.mMajorVersion = majorVersion; + this.mMinorVersion = minorVersion; + this.mPatchVersion = patchVersion; + if (properties != null) { + this.mProperties = properties; + this.mWidth = getInt(DOC_WIDTH, 256); + this.mHeight = getInt(DOC_HEIGHT, 256); + this.mDensity = getFloat(DOC_DENSITY_AT_GENERATION, 0); + } + } + + private int getInt(int key, int defaultValue) { + Integer value = (Integer) mProperties.get(key); + if (value != null) { + return value; + } else { + return defaultValue; + } + } + + private long getLong(int key, long defaultValue) { + Long value = (Long) mProperties.get(key); + if (value != null) { + return value; + } else { + return defaultValue; + } + } + + private float getFloat(int key, float defaultValue) { + Float value = (Float) mProperties.get(key); + if (value != null) { + return value; + } else { + return defaultValue; + } + } + + private String getString(int key, String defaultValue) { + String value = (String) mProperties.get(key); + if (value != null) { + return value; + } else { + return defaultValue; + } + } + @Override public void write(@NonNull WireBuffer buffer) { apply(buffer, mWidth, mHeight, mDensity, mCapabilities); @@ -90,6 +207,16 @@ public class Header extends Operation implements RemoteComposeOperation { @NonNull @Override public String toString() { + String prop = ""; + if (mProperties != null) { + for (int i = 0; i < KEYS.length; i++) { + Object p = mProperties.get(KEYS[i]); + if (p != null) { + prop += "\n " + KEY_NAMES[i] + " " + p.toString(); + } + } + return "HEADER v" + mMajorVersion + "." + mMinorVersion + "." + mPatchVersion + prop; + } return "HEADER v" + mMajorVersion + "." @@ -102,12 +229,20 @@ public class Header extends Operation implements RemoteComposeOperation { + mHeight + " [" + mCapabilities - + "]"; + + "]" + + prop; } @Override public void apply(@NonNull RemoteContext context) { - context.header(mMajorVersion, mMinorVersion, mPatchVersion, mWidth, mHeight, mCapabilities); + context.header( + mMajorVersion, + mMinorVersion, + mPatchVersion, + mWidth, + mHeight, + mCapabilities, + mProperties); } @NonNull @@ -157,22 +292,44 @@ public class Header extends Operation implements RemoteComposeOperation { } /** - * Read this operation and add it to the list of operations + * Apply the header to the wire buffer * - * @param buffer the buffer to read - * @param operations the list of operations that will be added to + * @param buffer */ - public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { - int majorVersion = buffer.readInt(); - int minorVersion = buffer.readInt(); - int patchVersion = buffer.readInt(); - int width = buffer.readInt(); - int height = buffer.readInt(); - // float density = buffer.readFloat(); - float density = 1f; - long capabilities = buffer.readLong(); - Header header = - new Header( + public static void apply(@NonNull WireBuffer buffer, short[] type, Object[] value) { + buffer.start(OP_CODE); + buffer.writeInt(MAJOR_VERSION | MAGIC_NUMBER); // major version number of the protocol + buffer.writeInt(MINOR_VERSION); // minor version number of the protocol + buffer.writeInt(PATCH_VERSION); // patch version number of the protocol + buffer.writeInt(type.length); + writeMap(buffer, type, value); + } + + /** + * @param is the stream to read from + * @return the header + * @throws IOException if there is an error reading the header + */ + public static Header readDirect(InputStream is) throws IOException { + DataInputStream stream = new DataInputStream(is); + try { + + int type = stream.readByte(); + + if (type != OP_CODE) { + throw new IOException("Invalid header " + type + " != " + OP_CODE); + } + int majorVersion = stream.readInt(); + int minorVersion = stream.readInt(); + int patchVersion = stream.readInt(); + + if (majorVersion < 0x10000) { + int width = stream.readInt(); + int height = stream.readInt(); + // float density = is.read(); + float density = 1f; + long capabilities = stream.readLong(); + return new Header( majorVersion, minorVersion, patchVersion, @@ -180,7 +337,173 @@ public class Header extends Operation implements RemoteComposeOperation { height, density, capabilities); - operations.add(header); + } + + if ((majorVersion & 0xFFFF0000) != MAGIC_NUMBER) { + throw new IOException( + "Invalid header MAGIC_NUMBER " + + (majorVersion & 0xFFFF0000) + + " != " + + MAGIC_NUMBER); + } + majorVersion &= 0xFFFF; + int len = stream.readInt(); + short[] types = new short[len]; + Object[] values = new Object[len]; + readMap(stream, types, values); + IntMap<Object> map = new IntMap<>(); + for (int i = 0; i < len; i++) { + map.put(types[i], values[i]); + } + return new Header(majorVersion, minorVersion, patchVersion, map); + + } finally { + stream.close(); + } + } + + /** + * Read this operation and add it to the list of operations + * + * @param stream the buffer to read + * @param types the list of types that will be populated + * @param values the list of values that will be populated + */ + private static void readMap(DataInputStream stream, short[] types, Object[] values) + throws IOException { + for (int i = 0; i < types.length; i++) { + short tag = (short) stream.readShort(); + int itemLen = stream.readShort(); + int dataType = tag >> 10; + types[i] = (short) (tag & 0x3F); + Object value; + switch (dataType) { + case DATA_TYPE_INT: + values[i] = stream.readInt(); + break; + case DATA_TYPE_FLOAT: + values[i] = stream.readFloat(); + break; + case DATA_TYPE_LONG: + values[i] = stream.readLong(); + break; + case DATA_TYPE_STRING: + int slen = stream.readInt(); + byte[] data = new byte[slen]; + stream.readFully(data); + values[i] = new String(data); + break; + } + } + } + + /** + * 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 majorVersion = buffer.readInt(); + int minorVersion = buffer.readInt(); + int patchVersion = buffer.readInt(); + if (majorVersion < 0x10000) { + int width = buffer.readInt(); + int height = buffer.readInt(); + // float density = buffer.readFloat(); + float density = 1f; + long capabilities = buffer.readLong(); + Header header = + new Header( + majorVersion, + minorVersion, + patchVersion, + width, + height, + density, + capabilities); + operations.add(header); + } else { + majorVersion &= 0xFFFF; + int length = buffer.readInt(); + short[] types = new short[length]; + Object[] values = new Object[length]; + readMap(buffer, types, values); + IntMap<Object> map = new IntMap<>(); + for (int i = 0; i < length; i++) { + map.put(types[i], values[i]); + } + Header header = new Header(majorVersion, minorVersion, patchVersion, map); + operations.add(header); + } + } + + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param types the list of types that will be populated + * @param values the list of values that will be populated + */ + private static void readMap(@NonNull WireBuffer buffer, short[] types, Object[] values) { + for (int i = 0; i < types.length; i++) { + short tag = (short) buffer.readShort(); + int itemLen = buffer.readShort(); + int dataType = tag >> 10; + types[i] = (short) (tag & 0x3F); + Object value; + switch (dataType) { + case DATA_TYPE_INT: + values[i] = buffer.readInt(); + break; + case DATA_TYPE_FLOAT: + values[i] = buffer.readFloat(); + break; + case DATA_TYPE_LONG: + values[i] = buffer.readLong(); + break; + case DATA_TYPE_STRING: + values[i] = buffer.readUTF8(); + break; + } + } + } + + /** + * Write the map of values to the buffer + * + * @param buffer the buffer to read + * @param types the list of types that will be written + * @param values the list of values that will be written + */ + private static void writeMap(@NonNull WireBuffer buffer, short[] types, Object[] values) { + for (int i = 0; i < types.length; i++) { + short tag = types[i]; + if (values[i] instanceof String) { + tag = (short) (tag | (DATA_TYPE_STRING << 10)); + buffer.writeShort(tag); + String str = (String) values[i]; + byte[] data = str.getBytes(); + buffer.writeShort((data.length + 4)); + buffer.writeBuffer(data); + } else if (values[i] instanceof Integer) { + tag = (short) (tag | (DATA_TYPE_INT << 10)); + buffer.writeShort(tag); + buffer.writeShort(4); + buffer.writeInt((Integer) values[i]); + } else if (values[i] instanceof Float) { + tag = (short) (tag | (DATA_TYPE_FLOAT << 10)); + buffer.writeShort(tag); + buffer.writeShort(4); + + buffer.writeFloat((float) values[i]); + } else if (values[i] instanceof Long) { + tag = (short) (tag | (DATA_TYPE_LONG << 10)); + buffer.writeShort(tag); + buffer.writeShort(8); + buffer.writeLong((Long) values[i]); + } + } } /** diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java index f04f30dc62fb..2a5260c0c9b1 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java @@ -29,6 +29,9 @@ 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.IntegerExpressionEvaluator; +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.Arrays; import java.util.List; @@ -38,7 +41,7 @@ import java.util.List; * like injecting the width of the component int draw rect As well as supporting generalized * animation floats. The floats represent a RPN style calculator */ -public class IntegerExpression extends Operation implements VariableSupport { +public class IntegerExpression extends Operation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.INTEGER_EXPRESSION; private static final String CLASS_NAME = "IntegerExpression"; public int mId; @@ -225,4 +228,14 @@ public class IntegerExpression extends Operation implements VariableSupport { public static boolean isId(int mask, int i, int value) { return ((1 << i) & mask) != 0 && value < IntegerExpressionEvaluator.OFFSET; } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .addTags(SerializeTags.EXPRESSION) + .add("type", CLASS_NAME) + .add("id", mId) + .add("mask", mId) + .addIntExpressionSrc("srcValues", mSrcValue, mMask); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java index dde632e0c346..96628fd51225 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java @@ -26,11 +26,13 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Operation to deal with Text data */ -public class NamedVariable extends Operation { +public class NamedVariable extends Operation implements Serializable { private static final int OP_CODE = Operations.NAMED_VARIABLE; private static final String CLASS_NAME = "NamedVariable"; public final int mVarId; @@ -135,4 +137,28 @@ public class NamedVariable extends Operation { public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("varId", mVarId) + .add("varName", mVarName) + .add("varType", typeToString()); + } + + private String typeToString() { + switch (mVarType) { + case COLOR_TYPE: + return "COLOR_TYPE"; + case FLOAT_TYPE: + return "FLOAT_TYPE"; + case STRING_TYPE: + return "STRING_TYPE"; + case IMAGE_TYPE: + return "IMAGE_TYPE"; + default: + return "INVALID_TYPE"; + } + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java index f756b76b86c3..8389aa707ee6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java @@ -29,11 +29,13 @@ 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.paint.PaintBundle; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Paint data operation */ -public class PaintData extends PaintOperation implements VariableSupport { +public class PaintData extends PaintOperation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.PAINT_VALUES; private static final String CLASS_NAME = "PaintData"; @NonNull public PaintBundle mPaintData = new PaintBundle(); @@ -126,4 +128,9 @@ public class PaintData extends PaintOperation implements VariableSupport { public void paint(@NonNull PaintContext context) { context.applyPaint(mPaintData); } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("paintBundle", mPaintData); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java index e7cce03f0c4b..8f353ce4a26b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java @@ -30,11 +30,13 @@ 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.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.Arrays; import java.util.List; -public class PathAppend extends PaintOperation implements VariableSupport { +public class PathAppend extends PaintOperation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.PATH_ADD; private static final String CLASS_NAME = "PathAppend"; int mInstanceId; @@ -253,4 +255,12 @@ public class PathAppend extends PaintOperation implements VariableSupport { } return str.toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("id", mInstanceId) + .add("path", pathString(mFloatPath)); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java index 1f76639b1b1f..7aa3390b53ee 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java @@ -29,11 +29,13 @@ 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.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.Arrays; import java.util.List; -public class PathCreate extends PaintOperation implements VariableSupport { +public class PathCreate extends PaintOperation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.PATH_CREATE; private static final String CLASS_NAME = "PathCreate"; int mInstanceId; @@ -237,4 +239,12 @@ public class PathCreate extends PaintOperation implements VariableSupport { public void apply(@NonNull RemoteContext context) { context.loadPathData(mInstanceId, mOutputPath); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("id", mInstanceId) + .add("path", pathString(mFloatPath)); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java index 65adfeabefa6..c5add57d4dd0 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathTween.java @@ -29,11 +29,13 @@ 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.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Operation to deal with Path data */ -public class PathTween extends PaintOperation implements VariableSupport { +public class PathTween extends PaintOperation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.PATH_TWEEN; private static final String CLASS_NAME = "PathTween"; public int mOutId; @@ -155,4 +157,14 @@ public class PathTween extends PaintOperation implements VariableSupport { public void paint(PaintContext context) { context.tweenPath(mOutId, mPathId1, mPathId2, mTweenOut); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("outId", mOutId) + .add("pathId1", mPathId1) + .add("pathId2", mPathId2) + .add("tween", mTween, mTweenOut); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java index c1ddf63264fa..a6a8a810d2ad 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java @@ -25,12 +25,14 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Describe a content description for the document */ public class RootContentDescription extends Operation - implements RemoteComposeOperation, AccessibleComponent { + implements RemoteComposeOperation, AccessibleComponent, Serializable { private static final int OP_CODE = Operations.ROOT_CONTENT_DESCRIPTION; private static final String CLASS_NAME = "RootContentDescription"; int mContentDescription; @@ -128,4 +130,9 @@ public class RootContentDescription extends Operation .description("Content description of root") .field(DocumentedOperation.INT, "id", "id of Int"); } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("contentDescriptionId", mContentDescription); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java index 46a32905b96c..5f6162b68e9e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java @@ -32,6 +32,8 @@ 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.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.Arrays; import java.util.HashMap; @@ -41,7 +43,7 @@ import java.util.List; * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is * compressed and saved in playback the image is decompressed */ -public class ShaderData extends Operation implements VariableSupport { +public class ShaderData extends Operation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.DATA_SHADER; private static final String CLASS_NAME = "ShaderData"; int mShaderTextId; // the actual text of a shader @@ -384,4 +386,15 @@ public class ShaderData extends Operation implements VariableSupport { public void enable(boolean shaderValid) { mShaderValid = shaderValid; } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("shaderTextId", mShaderTextId) + .add("shaderID", mShaderID) + .add("uniformRawFloatMap", mUniformRawFloatMap) + .add("uniformFloatMap", mUniformFloatMap) + .add("uniformBitmapMap", mUniformBitmapMap); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextAttribute.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextAttribute.java index 45cced3f8b45..3e72995de9db 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextAttribute.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextAttribute.java @@ -27,11 +27,13 @@ import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.PaintOperation; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Operation to Measure Text data */ -public class TextAttribute extends PaintOperation { +public class TextAttribute extends PaintOperation implements Serializable { private static final int OP_CODE = Operations.ATTRIBUTE_TEXT; private static final String CLASS_NAME = "TextMeasure"; public int mId; @@ -167,4 +169,34 @@ public class TextAttribute extends PaintOperation { break; } } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("id", mId) + .add("textId", mTextId) + .add("measureType", typeToString()); + } + + private String typeToString() { + switch (mType) { + case MEASURE_WIDTH: + return "MEASURE_WIDTH"; + case MEASURE_HEIGHT: + return "MEASURE_HEIGHT"; + case MEASURE_LEFT: + return "MEASURE_LEFT"; + case MEASURE_RIGHT: + return "MEASURE_RIGHT"; + case MEASURE_TOP: + return "MEASURE_TOP"; + case MEASURE_BOTTOM: + return "MEASURE_BOTTOM"; + case TEXT_LENGTH: + return "TEXT_LENGTH"; + default: + return "INVALID_TYPE"; + } + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java index 5788d8f4da64..419e6d074479 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java @@ -27,11 +27,13 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Operation to deal with Text data */ -public class TextData extends Operation implements SerializableToString { +public class TextData extends Operation implements SerializableToString, Serializable { private static final int OP_CODE = Operations.DATA_TEXT; private static final String CLASS_NAME = "TextData"; public final int mTextId; @@ -131,4 +133,9 @@ public class TextData extends Operation implements SerializableToString { private String getSerializedName() { return "DATA_TEXT"; } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("textId", mTextId).add("text", mText); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java index cc0ff025f09b..6b2f49be76f0 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java @@ -28,6 +28,8 @@ 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.StringUtils; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; @@ -36,7 +38,7 @@ import java.util.List; * [command][textID][before,after][flags] before and after define number of digits before and after * the decimal point */ -public class TextFromFloat extends Operation implements VariableSupport { +public class TextFromFloat extends Operation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.TEXT_FROM_FLOAT; private static final String CLASS_NAME = "TextFromFloat"; public int mTextId; @@ -209,4 +211,15 @@ public class TextFromFloat extends Operation implements VariableSupport { public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("textId", mTextId) + .add("value", mValue, mOutValue) + .add("digitsBefore", mDigitsBefore) + .add("digitsAfter", mDigitsAfter) + .add("flags", mFlags); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java index dceb8b67ec3a..e8865c26db12 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java @@ -26,6 +26,8 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; @@ -34,7 +36,7 @@ import java.util.List; * [command][textID][before,after][flags] before and after define number of digits before and after * the decimal point */ -public class TextLookup extends Operation implements VariableSupport { +public class TextLookup extends Operation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.TEXT_LOOKUP; private static final String CLASS_NAME = "TextFromFloat"; public int mTextId; @@ -150,4 +152,13 @@ public class TextLookup extends Operation implements VariableSupport { public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("textId", mTextId) + .add("dataSetId", mDataSetId) + .add("indexId", mIndex, mOutIndex); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java index 823b70656c86..de2025569d46 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java @@ -26,11 +26,13 @@ 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.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Operation convert int index of a list to text */ -public class TextLookupInt extends Operation implements VariableSupport { +public class TextLookupInt extends Operation implements VariableSupport, Serializable { private static final int OP_CODE = Operations.TEXT_LOOKUP_INT; private static final String CLASS_NAME = "TextFromINT"; public int mTextId; @@ -143,4 +145,13 @@ public class TextLookupInt extends Operation implements VariableSupport { public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("textId", mTextId) + .add("dataSetId", mDataSetId) + .add("indexId", mIndex, mOutIndex); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java index d69561566b56..262916dd9d0c 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java @@ -25,11 +25,13 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Operation to deal with Text data */ -public class TextMerge extends Operation { +public class TextMerge extends Operation implements Serializable { private static final int OP_CODE = Operations.TEXT_MERGE; private static final String CLASS_NAME = "TextMerge"; public int mTextId; @@ -126,4 +128,13 @@ public class TextMerge extends Operation { public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("id", mTextId) + .add("leftId", mSrcId1) + .add("rightId", mSrcId2); + } } 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 997628140c46..2591a4c39778 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 @@ -35,6 +35,8 @@ import com.android.internal.widget.remotecompose.core.operations.layout.RootLayo import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression; import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap; import com.android.internal.widget.remotecompose.core.operations.utilities.touch.VelocityEasing; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.Arrays; import java.util.List; @@ -44,7 +46,8 @@ import java.util.List; * touch behaviours. Including animating to Notched, positions. and tweaking the dynamics of the * animation. */ -public class TouchExpression extends Operation implements VariableSupport, TouchListener { +public class TouchExpression extends Operation + implements VariableSupport, TouchListener, Serializable { private static final int OP_CODE = Operations.TOUCH_EXPRESSION; private static final String CLASS_NAME = "TouchExpression"; private float mDefValue; @@ -709,4 +712,16 @@ public class TouchExpression extends Operation implements VariableSupport, Touch public String deepToString(@NonNull String indent) { return indent + toString(); } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("id", mId) + .add("mDefValue", mDefValue, mOutDefValue) + .add("min", mMin, mOutMin) + .add("max", mMax, mOutMax) + .add("mode", mMode) + .addFloatExpressionSrc("srcExp", mSrcExp); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseProcess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseProcess.java index f896f3d8ee9c..8c9dd76c9ed5 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseProcess.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseProcess.java @@ -25,12 +25,15 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.ArrayList; import java.util.List; /** Represents the repeating part of an Impulse. */ -public class ImpulseProcess extends PaintOperation implements VariableSupport, Container { +public class ImpulseProcess extends PaintOperation + implements VariableSupport, Container, Serializable { private static final int OP_CODE = Operations.IMPULSE_PROCESS; private static final String CLASS_NAME = "ImpulseProcess"; @@ -151,4 +154,9 @@ public class ImpulseProcess extends PaintOperation implements VariableSupport, C public int estimateIterations() { return 1; } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("list", mList); + } } 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 0f4cf562eae6..2b63cf246555 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 @@ -27,12 +27,16 @@ 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.Utils; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.ArrayList; import java.util.List; /** Represents a loop of operations */ -public class LoopOperation extends PaintOperation implements Container, VariableSupport { +public class LoopOperation extends PaintOperation + implements Container, VariableSupport, Serializable { + private static final String CLASS_NAME = "LoopOperation"; private static final int OP_CODE = Operations.LOOP_START; @@ -198,4 +202,16 @@ public class LoopOperation extends PaintOperation implements Container, Variable } return 10; // this is a generic estmate if the values are variables; } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", CLASS_NAME) + .add("indexVariableId", mIndexVariableId) + .add("until", mUntil, mUntilOut) + .add("from", mFrom, mFromOut) + .add("step", mStep, mStepOut) + .add("mUntilOut", mUntilOut) + .add("list", mList); + } } 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 1cfb50724e0b..d5db74b5ca51 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 @@ -209,7 +209,7 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access mPaint.setColor(mColor); mPaint.setTextSize(mFontSize); mPaint.setTextStyle(mType, (int) mFontWeight, mFontStyle == 1); - context.applyPaint(mPaint); + context.replacePaint(mPaint); if (mCachedString == null) { return; } @@ -330,7 +330,7 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access mPaint.setTextSize(mFontSize); mPaint.setTextStyle(mType, (int) mFontWeight, mFontStyle == 1); mPaint.setColor(mColor); - context.applyPaint(mPaint); + context.replacePaint(mPaint); float[] bounds = new float[4]; if (mCachedString == null) { return; @@ -343,7 +343,7 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access flags |= PaintContext.TEXT_COMPLEX; } context.getTextBounds(mTextId, 0, mCachedString.length(), flags, bounds); - if (bounds[2] - bounds[1] > maxWidth) { + if (bounds[2] - bounds[1] > maxWidth && mMaxLines > 1) { mComputedTextLayout = context.layoutComplexText( mTextId, diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java index dc1b875f0f9c..fd5f8c9cdbe7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java @@ -196,7 +196,7 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation { mPaint.reset(); mPaint.setStyle(PaintBundle.STYLE_FILL); mPaint.setColor(mR, mG, mB, mA); - context.applyPaint(mPaint); + context.replacePaint(mPaint); if (mShapeType == ShapeType.RECTANGLE) { context.drawRect(0f, 0f, mWidth, mHeight); } else if (mShapeType == ShapeType.CIRCLE) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java index 3acbd88329c2..e5f318307a75 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java @@ -252,7 +252,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation { paint.setColor(mR, mG, mB, mA); paint.setStrokeWidth(mBorderWidth); paint.setStyle(PaintBundle.STYLE_STROKE); - context.applyPaint(paint); + context.replacePaint(paint); if (mShapeType == ShapeType.RECTANGLE) { context.drawRect(0f, 0f, mWidth, mHeight); } else { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java index d3004aa73a77..69ace8478e08 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java @@ -126,7 +126,7 @@ public class RippleModifierOperation extends DecoratorModifierOperation implemen float radius = Math.max(mWidth, mHeight) * tweenRadius; mPaint.setColor(paintedColor); - context.applyPaint(mPaint); + context.replacePaint(mPaint); context.clipRect(0f, 0f, mWidth, mHeight); context.drawCircle(mAnimateRippleX, mAnimateRippleY, radius); context.restorePaint(); diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java index 4c7f503e0bf8..0f17b114133d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java @@ -15,6 +15,8 @@ */ package com.android.internal.widget.remotecompose.core.operations.paint; +import static com.android.internal.widget.remotecompose.core.serialize.MapSerializer.orderedOf; + import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,11 +25,16 @@ import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.operations.Utils; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.Map; /** Paint Bundle represents a delta of changes to a paint object */ -public class PaintBundle { +public class PaintBundle implements Serializable { @NonNull int[] mArray = new int[200]; @Nullable int[] mOutArray = null; int mPos = 0; @@ -337,7 +344,6 @@ public class PaintBundle { } } } - len = array[ret++]; // stops for (int j = 0; j < len; j++) { registerFloat(array[ret++], context, support); @@ -1239,4 +1245,201 @@ public class PaintBundle { return ret; } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", "PaintBundle"); + List<Map<String, Object>> list = new ArrayList<>(); + int i = 0; + while (i < mPos) { + int cmd = mArray[i++]; + int type = cmd & 0xFFFF; + switch (type) { + case TEXT_SIZE: + list.add(orderedOf("type", "TextSize", "size", getVariable(mArray[i++]))); + break; + case TYPEFACE: + int style = (cmd >> 16); + float weight = (float) (style & 0x3ff); + boolean italic = (style >> 10) > 0; + int fontFamily = mArray[i++]; + list.add(orderedOf("type", "FontFamily", "fontFamily", fontFamily)); + list.add(orderedOf("type", "FontWeight", "weight", weight)); + list.add(orderedOf("type", "TypeFace", "italic", italic)); + break; + case COLOR: + list.add(orderedOf("type", "Color", "color", colorInt(mArray[i++]))); + break; + case COLOR_ID: + list.add(orderedOf("type", "ColorId", "id", mArray[i++])); + break; + case STROKE_WIDTH: + list.add(orderedOf("type", "StrokeWidth", "width", getVariable(mArray[i++]))); + break; + case STROKE_MITER: + list.add(orderedOf("type", "StrokeMiter", "miter", getVariable(mArray[i++]))); + break; + case STROKE_CAP: + list.add(orderedOf("type", "StrokeCap", "cap", cmd >> 16)); + break; + case STYLE: + list.add(orderedOf("type", "Style", "style", cmd >> 16)); + break; + case COLOR_FILTER: + list.add( + orderedOf( + "type", + "ColorFilter", + "color", + colorInt(mArray[i++]), + "mode", + blendModeString(cmd >> 16))); + break; + case COLOR_FILTER_ID: + list.add( + orderedOf( + "type", + "ColorFilterID", + "id", + mArray[i++], + "mode", + blendModeString(cmd >> 16))); + break; + case CLEAR_COLOR_FILTER: + list.add(orderedOf("type", "ClearColorFilter")); + break; + case SHADER: + list.add(orderedOf("type", "Shader", "id", mArray[i++])); + break; + case ALPHA: + list.add(orderedOf("type", "Alpha", "alpha", getVariable(mArray[i++]))); + break; + case IMAGE_FILTER_QUALITY: + list.add(orderedOf("type", "ImageFilterQuality", "quality", cmd >> 16)); + break; + case BLEND_MODE: + list.add(orderedOf("type", "BlendMode", "mode", blendModeString(cmd >> 16))); + break; + case FILTER_BITMAP: + list.add(orderedOf("type", "FilterBitmap", "enabled", !(cmd >> 16 == 0))); + break; + case STROKE_JOIN: + list.add(orderedOf("type", "StrokeJoin", "strokeJoin", cmd >> 16)); + break; + case ANTI_ALIAS: + list.add(orderedOf("type", "AntiAlias", "enabled", !(cmd >> 16 == 0))); + break; + case GRADIENT: + i = serializeGradient(cmd, mArray, i, list); + } + } + serializer.add("operations", list); + } + + private static Map<String, Object> getVariable(int value) { + float fValue = Float.intBitsToFloat(value); + if (Float.isNaN(fValue)) { + return orderedOf("type", "Variable", "id", Utils.idFromNan(fValue)); + } + return orderedOf("type", "Value", "value", fValue); + } + + private static int serializeGradient( + int cmd, int[] array, int i, List<Map<String, Object>> list) { + int ret = i; + int gradientType = (cmd >> 16); + + int len = 0xFF & array[ret++]; // maximum 256 colors + + String[] colors = null; + if (len > 0) { + colors = new String[len]; + for (int j = 0; j < colors.length; j++) { + colors[j] = colorInt(array[ret++]); + } + } + len = array[ret++]; + float[] stops = null; + if (len > 0) { + stops = new float[len]; + for (int j = 0; j < colors.length; j++) { + stops[j] = Float.intBitsToFloat(array[ret++]); + } + } + + if (colors == null) { + return ret; + } + + int tileMode; + int centerX; + int centerY; + + switch (gradientType) { + case LINEAR_GRADIENT: + int startX = array[ret++]; + int startY = array[ret++]; + int endX = array[ret++]; + int endY = array[ret++]; + tileMode = array[ret++]; + list.add( + orderedOf( + "type", + "LinearGradient", + "colors", + colors, + "stops", + stops == null ? List.of() : stops, + "startX", + getVariable(startX), + "startY", + getVariable(startY), + "endX", + getVariable(endX), + "endY", + getVariable(endY), + "tileMode", + tileMode)); + break; + case RADIAL_GRADIENT: + centerX = array[ret++]; + centerY = array[ret++]; + int radius = array[ret++]; + tileMode = array[ret++]; + list.add( + orderedOf( + "type", + "LinearGradient", + "colors", + colors, + "stops", + stops == null ? List.of() : stops, + "centerX", + getVariable(centerX), + "centerY", + getVariable(centerY), + "radius", + getVariable(radius), + "tileMode", + tileMode)); + break; + case SWEEP_GRADIENT: + centerX = array[ret++]; + centerY = array[ret++]; + list.add( + orderedOf( + "type", + "LinearGradient", + "colors", + colors, + "stops", + stops == null ? List.of() : stops, + "centerX", + getVariable(centerX), + "centerY", + getVariable(centerY))); + } + + return ret; + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java index 65472c262206..cad76059f7a4 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java @@ -18,8 +18,11 @@ package com.android.internal.widget.remotecompose.core.operations.utilities.easi import android.annotation.NonNull; import android.annotation.Nullable; +import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; + /** Support Animation of the FloatExpression */ -public class FloatAnimation extends Easing { +public class FloatAnimation extends Easing implements Serializable { float[] mSpec; // mSpec[0] = duration // int(mSpec[1]) = num_of_param << 16 | type @@ -391,4 +394,14 @@ public class FloatAnimation extends Easing { public float getInitialValue() { return mInitialValue; } + + @Override + public void serialize(MapSerializer serializer) { + serializer + .add("type", "FloatAnimation") + .add("initialValue", mInitialValue) + .add("targetValue", mInitialValue) + .add("duration", mInitialValue) + .add("easing", Easing.getString(mEasingCurve.getType())); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java b/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java index bcdac22f7baa..f9ecf0f4f672 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java @@ -17,6 +17,7 @@ package com.android.internal.widget.remotecompose.core.serialize; import android.annotation.Nullable; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -24,6 +25,24 @@ import java.util.Map; public interface MapSerializer { /** + * Add a float expression + * + * @param key + * @param value + * @return + */ + MapSerializer addFloatExpressionSrc(String key, float[] value); + + /** + * Add an int expression + * + * @param key The key + * @param value The int src + * @param mask For determining ID from int + */ + MapSerializer addIntExpressionSrc(String key, int[] value, int mask); + + /** * Add metadata to this map for filtering by the data format generator. * * @param value A set of tags to add @@ -146,4 +165,19 @@ public interface MapSerializer { * @param value The Enum */ <T extends Enum<T>> MapSerializer add(String key, @Nullable Enum<T> value); + + /** + * Similar to Map.of, but create a LinkedHashMap preserving insertion order for predictable + * serialization. + * + * @param keysAndValues a even number of items, repeating String key and Object value. + * @return A LinkedHashMap. + */ + static LinkedHashMap<String, Object> orderedOf(Object... keysAndValues) { + final LinkedHashMap<String, Object> map = new LinkedHashMap<>(); + for (int i = 0; i < keysAndValues.length; i += 2) { + map.put((String) keysAndValues[i], keysAndValues[i + 1]); + } + return map; + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/serialize/SerializeTags.java b/core/java/com/android/internal/widget/remotecompose/core/serialize/SerializeTags.java index 99cac0fc75a8..c29dd98fbd7d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/serialize/SerializeTags.java +++ b/core/java/com/android/internal/widget/remotecompose/core/serialize/SerializeTags.java @@ -22,4 +22,5 @@ public enum SerializeTags { A11Y, ACTION, DRAW_OPERATION, + EXPRESSION, } diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java index 2c874b183a62..cb759a61249a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java @@ -25,11 +25,15 @@ 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.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Used to represent a boolean */ -public class BooleanConstant extends Operation { +public class BooleanConstant extends Operation implements Serializable { + private static final String CLASS_NAME = "BooleanConstant"; + private static final int OP_CODE = Operations.DATA_BOOLEAN; private boolean mValue = false; private int mId; @@ -124,4 +128,9 @@ public class BooleanConstant extends Operation { .field(DocumentedOperation.INT, "id", "id of Int") .field(BYTE, "value", "8-bit 0 or 1"); } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("id", mId).add("value", mValue); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java index 5462d3e069ed..c734f813ede3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java @@ -25,13 +25,17 @@ 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.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Represents a single integer typically used for states or named for input into the system */ -public class IntegerConstant extends Operation { - private int mValue = 0; - private int mId; +public class IntegerConstant extends Operation implements Serializable { + private static final String CLASS_NAME = "IntegerConstant"; + + private final int mValue; + private final int mId; IntegerConstant(int id, int value) { mId = id; @@ -116,4 +120,9 @@ public class IntegerConstant extends Operation { .field(DocumentedOperation.INT, "id", "id of Int") .field(INT, "value", "32-bit int value"); } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("id", mId).add("value", mValue); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java index 1a3cdb1a96d7..50509f3636b3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java +++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java @@ -25,14 +25,18 @@ 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.serialize.MapSerializer; +import com.android.internal.widget.remotecompose.core.serialize.Serializable; import java.util.List; /** Used to represent a long */ -public class LongConstant extends Operation { +public class LongConstant extends Operation implements Serializable { + private static final String CLASS_NAME = "LongConstant"; + private static final int OP_CODE = Operations.DATA_LONG; - private long mValue; - private int mId; + private final long mValue; + private final int mId; public LongConstant(int id, long value) { mId = id; @@ -107,4 +111,9 @@ public class LongConstant extends Operation { .field(DocumentedOperation.INT, "id", "id of Int") .field(LONG, "value", "The long Value"); } + + @Override + public void serialize(MapSerializer serializer) { + serializer.add("type", CLASS_NAME).add("id", mId).add("value", mValue); + } } 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 77f4b6a83eef..1d1e579ebc2f 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java +++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java @@ -29,7 +29,6 @@ import android.hardware.SensorManager; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; -import android.view.HapticFeedbackConstants; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; @@ -596,29 +595,34 @@ public class RemoteComposePlayer extends FrameLayout implements RemoteContextAwa } } - private static int[] sHapticTable = { - HapticFeedbackConstants.NO_HAPTICS, - HapticFeedbackConstants.LONG_PRESS, - HapticFeedbackConstants.VIRTUAL_KEY, - HapticFeedbackConstants.KEYBOARD_TAP, - HapticFeedbackConstants.CLOCK_TICK, - HapticFeedbackConstants.CONTEXT_CLICK, - HapticFeedbackConstants.KEYBOARD_PRESS, - HapticFeedbackConstants.KEYBOARD_RELEASE, - HapticFeedbackConstants.VIRTUAL_KEY_RELEASE, - HapticFeedbackConstants.TEXT_HANDLE_MOVE, - HapticFeedbackConstants.GESTURE_START, - HapticFeedbackConstants.GESTURE_END, - HapticFeedbackConstants.CONFIRM, - HapticFeedbackConstants.REJECT, - HapticFeedbackConstants.TOGGLE_ON, - HapticFeedbackConstants.TOGGLE_OFF, - HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE, - HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE, - HapticFeedbackConstants.DRAG_START, - HapticFeedbackConstants.SEGMENT_TICK, - HapticFeedbackConstants.SEGMENT_FREQUENT_TICK, - }; + private static final int[] sHapticTable; + + static { + sHapticTable = + new int[] { + android.view.HapticFeedbackConstants.NO_HAPTICS, + android.view.HapticFeedbackConstants.LONG_PRESS, + android.view.HapticFeedbackConstants.VIRTUAL_KEY, + android.view.HapticFeedbackConstants.KEYBOARD_TAP, + android.view.HapticFeedbackConstants.CLOCK_TICK, + android.view.HapticFeedbackConstants.CONTEXT_CLICK, + android.view.HapticFeedbackConstants.KEYBOARD_PRESS, + android.view.HapticFeedbackConstants.KEYBOARD_RELEASE, + android.view.HapticFeedbackConstants.VIRTUAL_KEY_RELEASE, + android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE, + android.view.HapticFeedbackConstants.GESTURE_START, + android.view.HapticFeedbackConstants.GESTURE_END, + android.view.HapticFeedbackConstants.CONFIRM, + android.view.HapticFeedbackConstants.REJECT, + android.view.HapticFeedbackConstants.TOGGLE_ON, + android.view.HapticFeedbackConstants.TOGGLE_OFF, + android.view.HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE, + android.view.HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE, + android.view.HapticFeedbackConstants.DRAG_START, + android.view.HapticFeedbackConstants.SEGMENT_TICK, + android.view.HapticFeedbackConstants.SEGMENT_FREQUENT_TICK, + }; + } private void provideHapticFeedback(int type) { performHapticFeedback(sHapticTable[type % sHapticTable.length]); 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 6b1a30a7545c..ac4a294b5e5e 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 @@ -246,6 +246,12 @@ public class AndroidPaintContext extends PaintContext { } @Override + public void replacePaint(PaintBundle paintBundle) { + mPaint.reset(); + applyPaint(paintBundle); + } + + @Override public void drawRoundRect( float left, float top, float right, float bottom, float radiusX, float radiusY) { mCanvas.drawRoundRect(left, top, right, bottom, radiusX, radiusY, mPaint); 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 f76794fc0372..4d2dd05ca603 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 @@ -31,6 +31,7 @@ import android.widget.FrameLayout; import com.android.internal.widget.remotecompose.core.CoreDocument; import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.operations.Header; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.Theme; import com.android.internal.widget.remotecompose.player.RemoteComposeDocument; @@ -105,10 +106,16 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta mARContext.setDensity(mDensity); mARContext.setUseChoreographer(true); setContentDescription(mDocument.getDocument().getContentDescription()); + updateClickAreas(); requestLayout(); mARContext.loadFloat(RemoteContext.ID_TOUCH_EVENT_TIME, -Float.MAX_VALUE); invalidate(); + Integer fps = (Integer) mDocument.getDocument().getProperty(Header.DOC_DESIRED_FPS); + if (fps != null && fps > 0) { + mMaxFrameRate = fps; + mMaxFrameDelay = (long) (1000 / mMaxFrameRate); + } } @Override |