diff options
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; |