Merge "Update to ToT RemoteCompose" into main
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index 39a2ab3..43118a0 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -30,7 +30,7 @@
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySemantics;
-import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics.Mode;
+import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent.Mode;
import java.util.HashSet;
import java.util.List;
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 60767ed..7c48aa60 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -123,6 +123,11 @@
return mWidth;
}
+ /**
+ * Set the viewport width of the document
+ *
+ * @param width document width
+ */
public void setWidth(int width) {
this.mWidth = width;
mRemoteComposeState.setWindowWidth(width);
@@ -132,6 +137,11 @@
return mHeight;
}
+ /**
+ * Set the viewport height of the document
+ *
+ * @param height document height
+ */
public void setHeight(int height) {
this.mHeight = height;
mRemoteComposeState.setWindowHeight(height);
@@ -395,6 +405,11 @@
// ============== Haptic support ==================
public interface HapticEngine {
+ /**
+ * Implements a haptic effect
+ *
+ * @param type the type of effect
+ */
void haptic(int type);
}
@@ -404,6 +419,11 @@
mHapticEngine = engine;
}
+ /**
+ * Execute an haptic command
+ *
+ * @param type the type of haptic pre-defined effect
+ */
public void haptic(int type) {
if (mHapticEngine != null) {
mHapticEngine.haptic(type);
@@ -412,12 +432,23 @@
// ============== Haptic support ==================
- public void appliedTouchOperation(Component operation) {
- mAppliedTouchOperations.add(operation);
+ /**
+ * To signal that the given component will apply the touch operation
+ *
+ * @param component the component applying the touch
+ */
+ public void appliedTouchOperation(Component component) {
+ mAppliedTouchOperations.add(component);
}
/** Callback interface for host actions */
public interface ActionCallback {
+ /**
+ * Callback for actions
+ *
+ * @param name the action name
+ * @param value the payload of the action
+ */
void onAction(@NonNull String name, Object value);
}
@@ -450,7 +481,14 @@
mActionListeners.clear();
}
+ /** Id Actions */
public interface IdActionCallback {
+ /**
+ * Callback on Id Actions
+ *
+ * @param id the actio id triggered
+ * @param metadata optional metadata
+ */
void onAction(int id, @Nullable String metadata);
}
@@ -530,10 +568,20 @@
return mTop;
}
+ /**
+ * Returns the width of the click area
+ *
+ * @return the width of the click area
+ */
public float width() {
return Math.max(0, mRight - mLeft);
}
+ /**
+ * Returns the height of the click area
+ *
+ * @return the height of the click area
+ */
public float height() {
return Math.max(0, mBottom - mTop);
}
@@ -816,6 +864,7 @@
for (ClickAreaRepresentation clickArea : mClickAreas) {
if (clickArea.mId == id) {
warnClickListeners(clickArea);
+ return;
}
}
for (IdActionCallback listener : mIdActionListeners) {
@@ -1127,6 +1176,11 @@
return count;
}
+ /**
+ * Returns a list of useful statistics for the runtime document
+ *
+ * @return
+ */
@NonNull
public String[] getStats() {
ArrayList<String> ret = new ArrayList<>();
@@ -1145,8 +1199,8 @@
values[0] += 1;
values[1] += sizeOfComponent(mOperation, buffer);
- if (mOperation instanceof Component) {
- Component com = (Component) mOperation;
+ if (mOperation instanceof Container) {
+ Container com = (Container) mOperation;
count += addChildren(com, map, buffer);
} else if (mOperation instanceof LoopOperation) {
LoopOperation com = (LoopOperation) mOperation;
@@ -1172,9 +1226,9 @@
}
private int addChildren(
- @NonNull Component base, @NonNull HashMap<String, int[]> map, @NonNull WireBuffer tmp) {
- int count = base.mList.size();
- for (Operation mOperation : base.mList) {
+ @NonNull Container base, @NonNull HashMap<String, int[]> map, @NonNull WireBuffer tmp) {
+ int count = base.getList().size();
+ for (Operation mOperation : base.getList()) {
Class<? extends Operation> c = mOperation.getClass();
int[] values;
if (map.containsKey(c.getSimpleName())) {
@@ -1185,65 +1239,42 @@
}
values[0] += 1;
values[1] += sizeOfComponent(mOperation, tmp);
- if (mOperation instanceof Component) {
- count += addChildren((Component) mOperation, map, tmp);
- }
- if (mOperation instanceof LoopOperation) {
- count += addChildren((LoopOperation) mOperation, map, tmp);
+ if (mOperation instanceof Container) {
+ count += addChildren((Container) mOperation, map, tmp);
}
}
return count;
}
- private int addChildren(
- @NonNull LoopOperation base,
- @NonNull HashMap<String, int[]> map,
- @NonNull WireBuffer tmp) {
- int count = base.mList.size();
- for (Operation mOperation : base.mList) {
- Class<? extends Operation> c = mOperation.getClass();
- int[] values;
- if (map.containsKey(c.getSimpleName())) {
- values = map.get(c.getSimpleName());
- } else {
- values = new int[2];
- map.put(c.getSimpleName(), values);
- }
- values[0] += 1;
- values[1] += sizeOfComponent(mOperation, tmp);
- if (mOperation instanceof Component) {
- count += addChildren((Component) mOperation, map, tmp);
- }
- if (mOperation instanceof LoopOperation) {
- count += addChildren((LoopOperation) mOperation, map, tmp);
- }
- }
- return count;
- }
-
+ /**
+ * Returns a string representation of the operations, traversing the list of operations &
+ * containers
+ *
+ * @return
+ */
@NonNull
public String toNestedString() {
StringBuilder ret = new StringBuilder();
for (Operation mOperation : mOperations) {
ret.append(mOperation.toString());
ret.append("\n");
- if (mOperation instanceof Component) {
- toNestedString((Component) mOperation, ret, " ");
+ if (mOperation instanceof Container) {
+ toNestedString((Container) mOperation, ret, " ");
}
}
return ret.toString();
}
private void toNestedString(
- @NonNull Component base, @NonNull StringBuilder ret, String indent) {
- for (Operation mOperation : base.mList) {
+ @NonNull Container base, @NonNull StringBuilder ret, String indent) {
+ for (Operation mOperation : base.getList()) {
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 + " ");
+ if (mOperation instanceof Container) {
+ toNestedString((Container) mOperation, ret, indent + " ");
}
}
}
@@ -1255,6 +1286,12 @@
/** defines if a shader can be run */
public interface ShaderControl {
+ /**
+ * validate if a shader can run in the document
+ *
+ * @param shader the source of the shader
+ * @return true if the shader is allowed to run
+ */
boolean isShaderValid(String shader);
}
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 d9f12cb..9a37a22 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -55,6 +55,8 @@
import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.PaintData;
+import com.android.internal.widget.remotecompose.core.operations.ParticlesCreate;
+import com.android.internal.widget.remotecompose.core.operations.ParticlesLoop;
import com.android.internal.widget.remotecompose.core.operations.PathAppend;
import com.android.internal.widget.remotecompose.core.operations.PathCreate;
import com.android.internal.widget.remotecompose.core.operations.PathData;
@@ -75,6 +77,8 @@
import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
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.ImpulseOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.ImpulseProcess;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
@@ -188,6 +192,11 @@
public static final int PATH_TWEEN = 158;
public static final int PATH_CREATE = 159;
public static final int PATH_ADD = 160;
+ public static final int PARTICLE_CREATE = 161;
+ public static final int PARTICLE_PROCESS = 162;
+ public static final int PARTICLE_LOOP = 163;
+ public static final int IMPULSE_START = 164;
+ public static final int IMPULSE_PROCESS = 165;
///////////////////////////////////////// ======================
@@ -366,6 +375,10 @@
map.put(PATH_TWEEN, PathTween::read);
map.put(PATH_CREATE, PathCreate::read);
map.put(PATH_ADD, PathAppend::read);
+ map.put(IMPULSE_START, ImpulseOperation::read);
+ map.put(IMPULSE_PROCESS, ImpulseProcess::read);
+ map.put(PARTICLE_CREATE, ParticlesCreate::read);
+ map.put(PARTICLE_LOOP, ParticlesLoop::read);
map.put(ACCESSIBILITY_SEMANTICS, CoreSemantics::read);
// map.put(ACCESSIBILITY_CUSTOM_ACTION, CoreSemantics::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index 4a40a31..49a0457 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -33,10 +33,16 @@
return mContext;
}
+ /**
+ * Returns true if the needsRepaint flag is set
+ *
+ * @return true if the document asks to be repainted
+ */
public boolean doesNeedsRepaint() {
return mNeedsRepaint;
}
+ /** Clear the needsRepaint flag */
public void clearNeedsRepaint() {
mNeedsRepaint = false;
}
@@ -65,6 +71,20 @@
matrixSave();
}
+ /**
+ * Draw a bitmap
+ *
+ * @param imageId
+ * @param srcLeft
+ * @param srcTop
+ * @param srcRight
+ * @param srcBottom
+ * @param dstLeft
+ * @param dstTop
+ * @param dstRight
+ * @param dstBottom
+ * @param cdId
+ */
public abstract void drawBitmap(
int imageId,
int srcLeft,
@@ -77,26 +97,105 @@
int dstBottom,
int cdId);
+ /**
+ * scale the following commands
+ *
+ * @param scaleX horizontal scale factor
+ * @param scaleY vertical scale factor
+ */
public abstract void scale(float scaleX, float scaleY);
+ /**
+ * Rotate the following commands
+ *
+ * @param translateX horizontal translation
+ * @param translateY vertical translation
+ */
public abstract void translate(float translateX, float translateY);
+ /**
+ * Draw an arc
+ *
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ * @param startAngle
+ * @param sweepAngle
+ */
public abstract void drawArc(
float left, float top, float right, float bottom, float startAngle, float sweepAngle);
+ /**
+ * Draw a sector
+ *
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ * @param startAngle
+ * @param sweepAngle
+ */
public abstract void drawSector(
float left, float top, float right, float bottom, float startAngle, float sweepAngle);
+ /**
+ * Draw a bitmap
+ *
+ * @param id
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
public abstract void drawBitmap(int id, float left, float top, float right, float bottom);
+ /**
+ * Draw a circle
+ *
+ * @param centerX
+ * @param centerY
+ * @param radius
+ */
public abstract void drawCircle(float centerX, float centerY, float radius);
+ /**
+ * Draw a line
+ *
+ * @param x1
+ * @param y1
+ * @param x2
+ * @param y2
+ */
public abstract void drawLine(float x1, float y1, float x2, float y2);
+ /**
+ * Draw an oval
+ *
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
public abstract void drawOval(float left, float top, float right, float bottom);
+ /**
+ * Draw a path
+ *
+ * @param id the path id
+ * @param start starting point of the path where we start drawing it
+ * @param end ending point of the path where we stop drawing it
+ */
public abstract void drawPath(int id, float start, float end);
+ /**
+ * Draw a rectangle
+ *
+ * @param left left coordinate of the rectangle
+ * @param top top coordinate of the rectangle
+ * @param right right coordinate of the rectangle
+ * @param bottom bottom coordinate of the rectangle
+ */
public abstract void drawRect(float left, float top, float right, float bottom);
/** this caches the paint to a paint stack */
@@ -105,9 +204,27 @@
/** This restores the paint form the paint stack */
public abstract void restorePaint();
+ /**
+ * draw a round rect
+ *
+ * @param left left coordinate of the rectangle
+ * @param top top coordinate of the rectangle
+ * @param right right coordinate of the rectangle
+ * @param bottom bottom coordinate of the rectangle
+ * @param radiusX horizontal radius of the rounded corner
+ * @param radiusY vertical radius of the rounded corner
+ */
public abstract void drawRoundRect(
float left, float top, float right, float bottom, float radiusX, float radiusY);
+ /**
+ * Draw the text glyphs on the provided path
+ *
+ * @param textId id of the text
+ * @param pathId id of the path
+ * @param hOffset horizontal offset
+ * @param vOffset vertical offset
+ */
public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset);
/**
@@ -158,6 +275,14 @@
public abstract void drawTweenPath(
int path1Id, int path2Id, float tween, float start, float stop);
+ /**
+ * Interpolate between two path and return the resulting path
+ *
+ * @param out the interpolated path
+ * @param path1 start path
+ * @param path2 end path
+ * @param tween interpolation value from 0 (start path) to 1 (end path)
+ */
public abstract void tweenPath(int out, int path1, int path2, float tween);
/**
@@ -275,12 +400,33 @@
System.out.println("[LOG] " + content);
}
+ /** Indicates the document needs to be repainted */
public void needsRepaint() {
mNeedsRepaint = true;
}
+ /**
+ * Starts a graphics layer
+ *
+ * @param w
+ * @param h
+ */
public abstract void startGraphicsLayer(int w, int h);
+ /**
+ * Starts a graphics layer
+ *
+ * @param scaleX
+ * @param scaleY
+ * @param rotationX
+ * @param rotationY
+ * @param rotationZ
+ * @param shadowElevation
+ * @param transformOriginX
+ * @param transformOriginY
+ * @param alpha
+ * @param renderEffectId
+ */
public abstract void setGraphicsLayer(
float scaleX,
float scaleY,
@@ -293,6 +439,7 @@
float alpha,
int renderEffectId);
+ /** Ends a graphics layer */
public abstract void endGraphicsLayer();
public boolean isVisualDebug() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
index cfdd522..f355676 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
@@ -39,6 +39,11 @@
return indent + toString();
}
+ /**
+ * Paint the operation in the context
+ *
+ * @param context painting context
+ */
public abstract void paint(@NonNull PaintContext context);
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Platform.java b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
index dcb8efeb..e4a063d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Platform.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
@@ -20,13 +20,38 @@
/** Services that are needed to be provided by the platform during encoding. */
public interface Platform {
+
+ /**
+ * Converts a platform-specific image object into a platform-independent byte buffer
+ *
+ * @param image
+ * @return
+ */
@Nullable
byte[] imageToByteArray(@NonNull Object image);
+ /**
+ * Returns the width of a platform-specific image object
+ *
+ * @param image platform-specific image object
+ * @return the width of the image in pixels
+ */
int getImageWidth(@NonNull Object image);
+ /**
+ * Returns the height of a platform-specific image object
+ *
+ * @param image platform-specific image object
+ * @return the height of the image in pixels
+ */
int getImageHeight(@NonNull Object image);
+ /**
+ * Converts a platform-specific path object into a platform-independent float buffer
+ *
+ * @param path
+ * @return
+ */
@Nullable
float[] pathToFloatArray(@NonNull Object path);
@@ -38,6 +63,12 @@
TODO,
}
+ /**
+ * Log a message
+ *
+ * @param category
+ * @param message
+ */
void log(LogCategory category, String message);
Platform None =
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 fc1e36d..39cc997 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -58,6 +58,8 @@
import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.PaintData;
+import com.android.internal.widget.remotecompose.core.operations.ParticlesCreate;
+import com.android.internal.widget.remotecompose.core.operations.ParticlesLoop;
import com.android.internal.widget.remotecompose.core.operations.PathAppend;
import com.android.internal.widget.remotecompose.core.operations.PathCreate;
import com.android.internal.widget.remotecompose.core.operations.PathData;
@@ -77,6 +79,8 @@
import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
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.ImpulseOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.ImpulseProcess;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
@@ -411,11 +415,7 @@
BitmapData.apply(
mBuffer, imageId, imageWidth, imageHeight, data); // todo: potential npe
}
- int contentDescriptionId = 0;
- if (contentDescription != null) {
- contentDescriptionId = addText(contentDescription);
- }
- DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId);
+ addDrawBitmap(imageId, left, top, right, bottom, contentDescription);
}
/**
@@ -441,6 +441,24 @@
}
/**
+ * @param imageId The Id bitmap to be drawn
+ * @param left left coordinate of rectangle that the bitmap will be to fit into
+ * @param top top coordinate of rectangle that the bitmap will be to fit into
+ * @param contentDescription content description of the image
+ */
+ public void addDrawBitmap(
+ int imageId, float left, float top, @Nullable String contentDescription) {
+ int imageWidth = mPlatform.getImageWidth(imageId);
+ int imageHeight = mPlatform.getImageHeight(imageId);
+ int contentDescriptionId = 0;
+ if (contentDescription != null) {
+ contentDescriptionId = addText(contentDescription);
+ }
+ DrawBitmap.apply(
+ mBuffer, imageId, left, top, imageWidth, imageHeight, contentDescriptionId);
+ }
+
+ /**
* @param image The bitmap to be drawn
* @param srcLeft left coordinate in the source bitmap will be to extracted
* @param srcTop top coordinate in the source bitmap will be to extracted
@@ -537,7 +555,7 @@
}
/**
- * This defines the name of the color given the id.
+ * This defines the name of the bitmap given the id.
*
* @param id of the Bitmap
* @param name Name of the color
@@ -2060,4 +2078,61 @@
public int nextId() {
return mRemoteComposeState.nextId();
}
+
+ private boolean mInImpulseProcess = false;
+
+ /**
+ * add an impulse. (must be followed by impulse end)
+ *
+ * @param duration duration of the impulse
+ * @param start the start time
+ */
+ public void addImpulse(float duration, float start) {
+ ImpulseOperation.apply(mBuffer, duration, start);
+ mInImpulseProcess = false;
+ }
+
+ /** add an impulse process */
+ public void addImpulseProcess() {
+ ImpulseProcess.apply(mBuffer);
+ mInImpulseProcess = true;
+ }
+
+ /** Add an impulse end */
+ public void addImpulseEnd() {
+ ContainerEnd.apply(mBuffer);
+ if (mInImpulseProcess) {
+ ContainerEnd.apply(mBuffer);
+ }
+ mInImpulseProcess = false;
+ }
+
+ /**
+ * Start a particle engine container & initial setup
+ *
+ * @param id the particle engine id
+ * @param varIds list of variable ids
+ * @param initialExpressions the expressions used to initialize the variables
+ * @param particleCount the number of particles to draw
+ */
+ public void addParticles(
+ int id, int[] varIds, float[][] initialExpressions, int particleCount) {
+ ParticlesCreate.apply(mBuffer, id, varIds, initialExpressions, particleCount);
+ }
+
+ /**
+ * Setup the particle engine loop
+ *
+ * @param id the particle engine id
+ * @param restart value on restart
+ * @param expressions the expressions used to update the variables during the particles run
+ */
+ public void addParticlesLoop(int id, float[] restart, float[][] expressions) {
+ ParticlesLoop.apply(mBuffer, id, restart, expressions);
+ }
+
+ /** Closes the particle engine container */
+ public void addParticleLoopEnd() {
+ ContainerEnd.apply(mBuffer);
+ }
}
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 63469aa..7d71584 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -45,7 +45,7 @@
new CoreDocument(); // todo: is this a valid way to initialize? bbade@
public @NonNull RemoteComposeState mRemoteComposeState =
new RemoteComposeState(); // todo, is this a valid use of RemoteComposeState -- bbade@
- long mStart = System.nanoTime(); // todo This should be set at a hi level
+
@Nullable protected PaintContext mPaintContext = null;
protected float mDensity = 2.75f;
@@ -71,6 +71,11 @@
return mDensity;
}
+ /**
+ * Set the density of the document
+ *
+ * @param density
+ */
public void setDensity(float density) {
if (density > 0) {
mDensity = density;
@@ -142,7 +147,6 @@
* @return a monotonic time in seconds (arbitrary zero point)
*/
public float getAnimationTime() {
- mAnimationTime = (System.nanoTime() - mStart) * 1E-9f; // Eliminate
return mAnimationTime;
}
@@ -243,6 +247,11 @@
public abstract @Nullable Object getObject(int mId);
+ /**
+ * Add a touch listener to the context
+ *
+ * @param touchExpression
+ */
public void addTouchListener(TouchListener touchExpression) {}
/**
@@ -337,6 +346,16 @@
// Operations
///////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Set the main information about a document
+ *
+ * @param majorVersion major version of the document protocol used
+ * @param minorVersion minor version of the document protocol used
+ * @param patchVersion patch version of the document protocol used
+ * @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)
+ */
public void header(
int majorVersion,
int minorVersion,
@@ -552,6 +571,15 @@
/** Defines when the last build was made */
public static final int ID_API_LEVEL = 28;
+ /** Defines when the TOUCH EVENT HAPPENED */
+ public static final int ID_TOUCH_EVENT_TIME = 29;
+
+ /** Animation time in seconds */
+ public static final int ID_ANIMATION_TIME = 30;
+
+ /** The delta between current and last Frame */
+ public static final int ID_ANIMATION_DELTA_TIME = 31;
+
public static final float FLOAT_DENSITY = Utils.asNan(ID_DENSITY);
/** CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 */
@@ -595,6 +623,15 @@
/** TOUCH_VEL_Y is the x velocity of the touch */
public static final float FLOAT_TOUCH_VEL_Y = Utils.asNan(ID_TOUCH_VEL_Y);
+ /** TOUCH_EVENT_TIME the time of the touch */
+ public static final float FLOAT_TOUCH_EVENT_TIME = Utils.asNan(ID_TOUCH_EVENT_TIME);
+
+ /** Animation time in seconds */
+ public static final float FLOAT_ANIMATION_TIME = Utils.asNan(ID_ANIMATION_TIME);
+
+ /** Animation time in seconds */
+ public static final float FLOAT_ANIMATION_DELTA_TIME = Utils.asNan(ID_ANIMATION_DELTA_TIME);
+
/** X acceleration sensor value in M/s^2 */
public static final float FLOAT_ACCELERATION_X = Utils.asNan(ID_ACCELERATION_X);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/SerializableToString.java b/core/java/com/android/internal/widget/remotecompose/core/SerializableToString.java
index 79ac789..3789621 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/SerializableToString.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/SerializableToString.java
@@ -20,5 +20,11 @@
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
public interface SerializableToString {
+ /**
+ * Returns a stable string representation of an operation
+ *
+ * @param indent the indentation for that operation
+ * @param serializer the serializer object
+ */
void serializeToString(int indent, @NonNull StringSerializer serializer);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java b/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java
index 611ba97..0a1be82 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+
/** Interface used by objects to register for touch events */
public interface TouchListener {
/**
@@ -45,4 +47,11 @@
* @param y the y coord of the drag
*/
void touchDrag(RemoteContext context, float x, float y);
+
+ /**
+ * Called after the touch event handler is inflated
+ *
+ * @param component component it is under
+ */
+ void setComponent(Component component);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
index 0174ce5..0972e05 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
@@ -18,11 +18,33 @@
import android.annotation.NonNull;
public interface DocumentationBuilder {
+
+ /**
+ * Add arbitrary text to the documentation
+ *
+ * @param value
+ */
void add(@NonNull String value);
+ /**
+ * Add the operation to the documentation
+ *
+ * @param category category of the operation
+ * @param id the OPCODE of the operation
+ * @param name the name of the operation
+ * @return a DocumentedOperation
+ */
@NonNull
DocumentedOperation operation(@NonNull String category, int id, @NonNull String name);
+ /**
+ * Add the operation to the documentation as a Work in Progress (WIP) operation
+ *
+ * @param category category of the operation
+ * @param id the OPCODE of the operation
+ * @param name the name of the operation
+ * @return a DocumentedOperation
+ */
@NonNull
DocumentedOperation wipOperation(@NonNull String category, int id, @NonNull String name);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedCompanionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedCompanionOperation.java
index 2806a5e..487ea2e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedCompanionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedCompanionOperation.java
@@ -18,5 +18,10 @@
import android.annotation.NonNull;
public interface DocumentedCompanionOperation {
+ /**
+ * A callback to populate the documentation of an operation
+ *
+ * @param doc the document being built
+ */
void documentation(@NonNull DocumentationBuilder doc);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
index bfab623..ffe8871 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
@@ -49,6 +49,12 @@
int mExamplesWidth = 100;
int mExamplesHeight = 100;
+ /**
+ * Returns the string representation of a field type
+ *
+ * @param type
+ * @return
+ */
@NonNull
public static String getType(int type) {
switch (type) {
@@ -117,6 +123,11 @@
return mVarSize;
}
+ /**
+ * Returns the size of the operation fields
+ *
+ * @return size in bytes
+ */
public int getSizeFields() {
int size = 0;
mVarSize = "";
@@ -152,12 +163,29 @@
return mExamplesHeight;
}
+ /**
+ * Document a field of the operation
+ *
+ * @param type
+ * @param name
+ * @param description
+ * @return
+ */
@NonNull
public DocumentedOperation field(int type, @NonNull String name, @NonNull String description) {
mFields.add(new OperationField(type, name, description));
return this;
}
+ /**
+ * Document a field of the operation
+ *
+ * @param type
+ * @param name
+ * @param varSize
+ * @param description
+ * @return
+ */
@NonNull
public DocumentedOperation field(
int type, @NonNull String name, @NonNull String varSize, @NonNull String description) {
@@ -165,6 +193,13 @@
return this;
}
+ /**
+ * Add possible values for the operation field
+ *
+ * @param name
+ * @param value
+ * @return
+ */
@NonNull
public DocumentedOperation possibleValues(@NonNull String name, int value) {
if (!mFields.isEmpty()) {
@@ -173,24 +208,50 @@
return this;
}
+ /**
+ * Add a description
+ *
+ * @param description
+ * @return
+ */
@NonNull
public DocumentedOperation description(@NonNull String description) {
mDescription = description;
return this;
}
+ /**
+ * Add arbitrary text as examples
+ *
+ * @param examples
+ * @return
+ */
@NonNull
public DocumentedOperation examples(@NonNull String examples) {
mTextExamples = examples;
return this;
}
+ /**
+ * Add an example image
+ *
+ * @param name the title of the image
+ * @param imagePath the path of the image
+ * @return
+ */
@NonNull
public DocumentedOperation exampleImage(@NonNull String name, @NonNull String imagePath) {
mExamples.add(new StringPair(name, imagePath));
return this;
}
+ /**
+ * Add examples with a given size
+ *
+ * @param width
+ * @param height
+ * @return
+ */
@NonNull
public DocumentedOperation examplesDimension(int width, int height) {
mExamplesWidth = width;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
index 9febcfe..e120f31 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
@@ -61,10 +61,21 @@
return mPossibleValues;
}
+ /**
+ * Add possible values for a field
+ *
+ * @param name
+ * @param value
+ */
public void possibleValue(@NonNull String name, @NonNull String value) {
mPossibleValues.add(new StringPair(name, value));
}
+ /**
+ * Return true if the field has enumerated values
+ *
+ * @return true if enumerated values, false otherwise
+ */
public boolean hasEnumeratedValues() {
return !mPossibleValues.isEmpty();
}
@@ -74,6 +85,11 @@
return mVarSize;
}
+ /**
+ * Returns the size in byte of the field depending on its type, or -1 if unknown
+ *
+ * @return the size in bytes
+ */
public int getSize() {
switch (mType) {
case DocumentedOperation.BYTE:
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 4c96025..98c2745 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
@@ -135,6 +135,15 @@
return OP_CODE;
}
+ /**
+ * Apply the header to the wire buffer
+ *
+ * @param buffer
+ * @param width
+ * @param height
+ * @param density
+ * @param capabilities
+ */
public static void apply(
@NonNull WireBuffer buffer, int width, int height, float density, long capabilities) {
buffer.start(OP_CODE);
@@ -193,6 +202,11 @@
.field(LONG, "CAPABILITIES", "Major version");
}
+ /**
+ * Set the version on a document
+ *
+ * @param document
+ */
public void setVersion(CoreDocument document) {
document.setVersion(mMajorVersion, mMinorVersion, mPatchVersion);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
new file mode 100644
index 0000000..9e891c4
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
@@ -0,0 +1,247 @@
+/*
+ * 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;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression.VAR1;
+
+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.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.operations.utilities.AnimatedFloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This creates a particle system. which consist of id, particleCount, array of id's and equations
+ * for constructing the particles
+ */
+public class ParticlesCreate extends Operation implements VariableSupport {
+ private static final int OP_CODE = Operations.PARTICLE_CREATE;
+ private static final String CLASS_NAME = "ParticlesCreate";
+ private final int mId;
+ private final float[][] mEquations;
+ private final float[][] mOutEquations;
+ private final float[][] mParticles;
+ private final int[] mIndexeVars; // the elements in mEquations that INDEXES
+ private final int mParticleCount;
+ private final int[] mVarId;
+ private static final int MAX_FLOAT_ARRAY = 2000;
+ private static final int MAX_EQU_LENGTH = 32;
+ @NonNull AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+
+ public ParticlesCreate(int id, int[] varId, float[][] values, int particleCount) {
+ mId = id;
+ mVarId = varId;
+ mEquations = values;
+ mParticleCount = particleCount;
+ mOutEquations = new float[values.length][];
+ for (int i = 0; i < values.length; i++) {
+ mOutEquations[i] = new float[values[i].length];
+ System.arraycopy(values[i], 0, mOutEquations[i], 0, values[i].length);
+ }
+ mParticles = new float[particleCount][varId.length];
+
+ int[] index = new int[20];
+ int indexes = 0;
+ int var1Int = Float.floatToRawIntBits(VAR1);
+ for (int j = 0; j < mEquations.length; j++) {
+ for (int k = 0; k < mEquations[j].length; k++) {
+ if (Float.isNaN(mEquations[j][k])
+ && Float.floatToRawIntBits(mEquations[j][k]) == var1Int) {
+ index[indexes++] = j * mEquations.length + k;
+ }
+ }
+ }
+ mIndexeVars = Arrays.copyOf(index, indexes);
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ for (int i = 0; i < mEquations.length; i++) {
+
+ for (int j = 0; j < mEquations[i].length; j++) {
+ float v = mEquations[i][j];
+ mOutEquations[i][j] =
+ (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v))
+ ? context.getFloat(Utils.idFromNan(v))
+ : v;
+ }
+ }
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ context.putObject(mId, this); // T
+ for (int i = 0; i < mEquations.length; i++) {
+ float[] mEquation = mEquations[i];
+ for (float v : mEquation) {
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mId, mVarId, mEquations, mParticleCount);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ String str = "ParticlesCreate[" + Utils.idString(mId) + "] ";
+ for (int j = 0; j < mVarId.length; j++) {
+ str += "[" + mVarId[j] + "] ";
+ float[] equation = mEquations[j];
+ String[] labels = new String[equation.length];
+ for (int i = 0; i < equation.length; i++) {
+ if (Float.isNaN(equation[i])) {
+ labels[i] = "[" + Utils.idStringFromNan(equation[i]) + "]";
+ }
+ }
+ str += AnimatedFloatExpression.toString(equation, labels) + "\n";
+ }
+
+ return str;
+ }
+
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param id
+ * @param varId
+ * @param equations
+ * @param particleCount
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int id,
+ @NonNull int[] varId,
+ @NonNull float[][] equations,
+ int particleCount) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeInt(particleCount);
+ buffer.writeInt(varId.length);
+ for (int i = 0; i < varId.length; i++) {
+ buffer.writeInt(varId[i]);
+ buffer.writeInt(equations[i].length);
+ for (int j = 0; j < equations[i].length; j++) {
+ buffer.writeFloat(equations[i][j]);
+ }
+ }
+ }
+
+ /**
+ * 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 id = buffer.readInt();
+ int particleCount = buffer.readInt();
+ int varLen = buffer.readInt();
+ if (varLen > MAX_FLOAT_ARRAY) {
+ throw new RuntimeException(varLen + " map entries more than max = " + MAX_FLOAT_ARRAY);
+ }
+ int[] varId = new int[varLen];
+ float[][] equations = new float[varLen][];
+ for (int i = 0; i < varId.length; i++) {
+ varId[i] = buffer.readInt();
+ int equLen = buffer.readInt();
+ if (equLen > MAX_EQU_LENGTH) {
+ throw new RuntimeException(
+ equLen + " map entries more than max = " + MAX_FLOAT_ARRAY);
+ }
+ equations[i] = new float[equLen];
+ for (int j = 0; j < equations[i].length; j++) {
+ equations[i][j] = buffer.readFloat();
+ }
+ }
+ ParticlesCreate data = new ParticlesCreate(id, varId, equations, particleCount);
+ operations.add(data);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+ .description("Creates a particle system")
+ .field(DocumentedOperation.INT, "id", "The reference of the particle system")
+ .field(INT, "particleCount", "number of particles to create")
+ .field(INT, "varLen", "number of variables asociate with the particles")
+ .field(FLOAT_ARRAY, "id", "varLen", "id followed by equations")
+ .field(INT, "equLen", "length of the equation")
+ .field(FLOAT_ARRAY, "equation", "varLen * equLen", "float array equations");
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return indent + toString();
+ }
+
+ void initializeParticle(int pNo) {
+ for (int j = 0; j < mParticles[pNo].length; j++) {
+ for (int k = 0; k < mIndexeVars.length; k++) {
+ int pos = mIndexeVars[k];
+ int jIndex = pos / mOutEquations.length;
+ int kIndex = pos % mOutEquations.length;
+ mOutEquations[jIndex][kIndex] = pNo;
+ }
+ mParticles[pNo][j] = mExp.eval(mOutEquations[j], mOutEquations[j].length);
+ }
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ for (int i = 0; i < mParticles.length; i++) {
+ initializeParticle(i);
+ }
+ }
+
+ public float[][] getParticles() {
+ return mParticles;
+ }
+
+ public int[] getVariableIds() {
+ return mVarId;
+ }
+
+ public float[][] getEquations() {
+ return mOutEquations;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java
new file mode 100644
index 0000000..7910790
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java
@@ -0,0 +1,295 @@
+/*
+ * 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;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Container;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This provides the mechinism to evolve the particles It consist of a restart equation and a list
+ * of equations particle restarts if restart equation > 0
+ */
+public class ParticlesLoop extends PaintOperation implements VariableSupport, Container {
+ private static final int OP_CODE = Operations.PARTICLE_LOOP;
+ private static final String CLASS_NAME = "ParticlesLoop";
+ private final int mId;
+ private final float[] mRestart;
+ private final float[] mOutRestart;
+ private final float[][] mEquations;
+ private final float[][] mOutEquations;
+ private int[] mVarId;
+ private float[][] mParticles;
+ private static final int MAX_FLOAT_ARRAY = 2000;
+ private static final int MAX_EQU_LENGTH = 32;
+ ParticlesCreate mParticlesSource;
+
+ @NonNull
+ @Override
+ public ArrayList<Operation> getList() {
+ return mList;
+ }
+
+ @NonNull private ArrayList<Operation> mList = new ArrayList<>();
+
+ @NonNull AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+
+ /**
+ * Create a new ParticlesLoop operation
+ *
+ * @param id of the create
+ * @param restart the restart equation kills and restart when positive
+ * @param values the loop equations
+ */
+ public ParticlesLoop(int id, float[] restart, float[][] values) {
+ mId = id;
+ mRestart = restart;
+ if (restart != null) {
+ mOutRestart = new float[restart.length];
+ System.arraycopy(restart, 0, mOutRestart, 0, restart.length);
+ } else {
+ mOutRestart = null;
+ }
+
+ mEquations = values;
+ mOutEquations = new float[values.length][];
+ for (int i = 0; i < values.length; i++) {
+ mOutEquations[i] = new float[values[i].length];
+ System.arraycopy(values[i], 0, mOutEquations[i], 0, values[i].length);
+ }
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ if (mOutRestart != null) {
+ for (int i = 0; i < mRestart.length; i++) {
+ float v = mRestart[i];
+ mOutRestart[i] =
+ (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v))
+ ? context.getFloat(Utils.idFromNan(v))
+ : v;
+ }
+ }
+ for (int i = 0; i < mEquations.length; i++) {
+ float[] mEquation = mEquations[i];
+ for (int j = 0; j < mEquation.length; j++) {
+ float v = mEquation[j];
+ mOutEquations[i][j] =
+ (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v))
+ ? context.getFloat(Utils.idFromNan(v))
+ : v;
+ }
+ }
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ mParticlesSource = (ParticlesCreate) context.getObject(mId);
+ mParticles = mParticlesSource.getParticles();
+ mVarId = mParticlesSource.getVariableIds();
+ if (mRestart != null) {
+ for (int i = 0; i < mRestart.length; i++) {
+ float v = mRestart[i];
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+ for (int i = 0; i < mEquations.length; i++) {
+ float[] mEquation = mEquations[i];
+ for (float v : mEquation) {
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mId, mRestart, mEquations);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ String str = "ParticlesLoop[" + Utils.idString(mId) + "] ";
+ return str;
+ }
+
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param id
+ * @param restart
+ * @param equations
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int id,
+ @Nullable float[] restart,
+ @NonNull float[][] equations) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ if (restart != null) {
+ buffer.writeInt(restart.length);
+ for (int i = 0; i < restart.length; i++) {
+ buffer.writeFloat(restart[i]);
+ }
+ } else {
+ buffer.writeInt(0);
+ }
+ buffer.writeInt(equations.length);
+ for (int i = 0; i < equations.length; i++) {
+ buffer.writeInt(equations[i].length);
+ for (int j = 0; j < equations[i].length; j++) {
+ buffer.writeFloat(equations[i][j]);
+ }
+ }
+ }
+
+ /**
+ * 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 id = buffer.readInt();
+ int restartLen = buffer.readInt();
+ float[] restart = null;
+ if (restartLen > 0) {
+ restart = new float[restartLen];
+ for (int i = 0; i < restartLen; i++) {
+ restart[i] = buffer.readFloat();
+ }
+ }
+
+ int varLen = buffer.readInt();
+ if (varLen > MAX_FLOAT_ARRAY) {
+ throw new RuntimeException(varLen + " map entries more than max = " + MAX_FLOAT_ARRAY);
+ }
+
+ float[][] equations = new float[varLen][];
+ for (int i = 0; i < varLen; i++) {
+
+ int equLen = buffer.readInt();
+ if (equLen > MAX_EQU_LENGTH) {
+ throw new RuntimeException(
+ equLen + " map entries more than max = " + MAX_FLOAT_ARRAY);
+ }
+ equations[i] = new float[equLen];
+ for (int j = 0; j < equations[i].length; j++) {
+ equations[i][j] = buffer.readFloat();
+ }
+ }
+ ParticlesLoop data = new ParticlesLoop(id, restart, equations);
+ operations.add(data);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+ .description("This evolves the particles & recycles them")
+ .field(DocumentedOperation.INT, "id", "id of particle system")
+ .field(
+ INT,
+ "recycleLen",
+ "the number of floats in restart equeation if 0 no restart")
+ .field(FLOAT_ARRAY, "values", "recycleLen", "array of floats")
+ .field(INT, "varLen", "the number of equations to follow")
+ .field(INT, "equLen", "the number of equations to follow")
+ .field(FLOAT_ARRAY, "values", "equLen", "floats for the equation");
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return indent + toString();
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {
+ RemoteContext remoteContext = context.getContext();
+ for (int i = 0; i < mParticles.length; i++) {
+ // Save the values to context TODO hand code the update
+ for (int j = 0; j < mParticles[i].length; j++) {
+ remoteContext.loadFloat(mVarId[j], mParticles[i][j]);
+ updateVariables(remoteContext);
+ }
+ // Evaluate the update function
+ for (int j = 0; j < mParticles[i].length; j++) {
+ mParticles[i][j] = mExp.eval(mOutEquations[j], mOutEquations[j].length);
+ remoteContext.loadFloat(mVarId[j], mParticles[i][j]);
+ }
+ // test for reset
+ if (mOutRestart != null) {
+ for (int k = 0; k < mRestart.length; k++) {
+ float v = mRestart[k];
+ mOutRestart[k] =
+ (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v))
+ ? remoteContext.getFloat(Utils.idFromNan(v))
+ : v;
+ }
+ if (mExp.eval(mOutRestart, mOutRestart.length) > 0) {
+ mParticlesSource.initializeParticle(i);
+ }
+ }
+
+ for (Operation op : mList) {
+ if (op instanceof VariableSupport) {
+ ((VariableSupport) op).updateVariables(context.getContext());
+ }
+
+ remoteContext.incrementOpCount();
+ op.apply(context.getContext());
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 55dd882..129201c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -219,6 +219,15 @@
return OP_CODE;
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param scroll
+ * @param alignment
+ * @param sizing
+ * @param mode
+ */
public static void apply(
@NonNull WireBuffer buffer, int scroll, int alignment, int sizing, int mode) {
buffer.start(OP_CODE);
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 ad86e0f..c1ddf63 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
@@ -95,6 +95,12 @@
return OP_CODE;
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param contentDescription
+ */
public static void apply(@NonNull WireBuffer buffer, int contentDescription) {
buffer.start(Operations.ROOT_CONTENT_DESCRIPTION);
buffer.writeInt(contentDescription);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
index e9aae1e..1698ecb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
@@ -92,6 +92,12 @@
return OP_CODE;
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param theme
+ */
public static void apply(@NonNull WireBuffer buffer, int theme) {
buffer.start(OP_CODE);
buffer.writeInt(theme);
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 3b293bd..9976281 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
@@ -368,6 +368,7 @@
*
* @param component the component, or null if outside
*/
+ @Override
public void setComponent(@Nullable Component component) {
mComponent = component;
if (mComponent != null) {
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 511858aa..abb7ad8 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
@@ -66,6 +66,12 @@
return "CANVAS_CONTENT";
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param componentId
+ */
public static void apply(@NonNull WireBuffer buffer, int componentId) {
buffer.start(Operations.LAYOUT_CANVAS_CONTENT);
buffer.writeInt(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 5f084e9..110bd30 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
@@ -77,6 +77,12 @@
return CoreSemantics.Mode.MERGE;
}
+ /**
+ * Animate ripple
+ *
+ * @param x starting position x of the ripple
+ * @param y starting position y of the ripple
+ */
public void animateRipple(float x, float y) {
mAnimateRippleStart = System.currentTimeMillis();
mAnimateRippleX = x;
@@ -212,6 +218,11 @@
return "ClickModifier";
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ */
public static void apply(@NonNull WireBuffer buffer) {
buffer.start(OP_CODE);
}
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 eee2aab..8e733ce 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
@@ -24,6 +24,7 @@
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.SerializableToString;
+import com.android.internal.widget.remotecompose.core.TouchListener;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
@@ -236,6 +237,7 @@
finalizeCreation();
}
+ /** Callback on component creation TODO: replace with inflate() */
public void finalizeCreation() {
for (Operation op : mList) {
if (op instanceof Component) {
@@ -273,6 +275,11 @@
context.mLastComponent = prev;
}
+ /**
+ * Add a component value to the component
+ *
+ * @param v
+ */
public void addComponentValue(@NonNull ComponentValue v) {
mComponentValues.add(v);
}
@@ -303,10 +310,10 @@
*/
public void inflate() {
for (Operation op : mList) {
- if (op instanceof TouchExpression) {
+ if (op instanceof TouchListener) {
// Make sure to set the component of a touch expression that belongs to us!
- TouchExpression touchExpression = (TouchExpression) op;
- touchExpression.setComponent(this);
+ TouchListener touchListener = (TouchListener) op;
+ touchListener.setComponent(this);
}
}
}
@@ -317,6 +324,11 @@
INVISIBLE
}
+ /**
+ * Returns true if the component is visible
+ *
+ * @return
+ */
public boolean isVisible() {
if (mVisibility != Visibility.VISIBLE || mParent == null) {
return mVisibility == Visibility.VISIBLE;
@@ -327,6 +339,11 @@
return true;
}
+ /**
+ * Set the visibility of the component
+ *
+ * @param visibility can be VISIBLE, INVISIBLE or GONE
+ */
public void setVisibility(@NonNull Visibility visibility) {
if (visibility != mVisibility || visibility != mScheduledVisibility) {
mScheduledVisibility = visibility;
@@ -443,6 +460,13 @@
@NonNull public float[] locationInWindow = new float[2];
+ /**
+ * Hit detection -- returns true if the point (x, y) is inside the component
+ *
+ * @param x
+ * @param y
+ * @return
+ */
public boolean contains(float x, float y) {
locationInWindow[0] = 0f;
locationInWindow[1] = 0f;
@@ -454,14 +478,32 @@
return x >= lx1 && x < lx2 && y >= ly1 && y < ly2;
}
+ /**
+ * Returns the horizontal scroll value of the content of this component
+ *
+ * @return 0 if no scroll
+ */
public float getScrollX() {
return 0;
}
+ /**
+ * Returns the vertical scroll value of the content of this component
+ *
+ * @return 0 if no scroll
+ */
public float getScrollY() {
return 0;
}
+ /**
+ * Click handler
+ *
+ * @param context
+ * @param document
+ * @param x
+ * @param y
+ */
public void onClick(
@NonNull RemoteContext context, @NonNull CoreDocument document, float x, float y) {
if (!contains(x, y)) {
@@ -479,6 +521,14 @@
}
}
+ /**
+ * Touch down handler
+ *
+ * @param context
+ * @param document
+ * @param x
+ * @param y
+ */
public void onTouchDown(RemoteContext context, CoreDocument document, float x, float y) {
if (!contains(x, y)) {
return;
@@ -501,6 +551,17 @@
}
}
+ /**
+ * Touch Up handler
+ *
+ * @param context
+ * @param document
+ * @param x
+ * @param y
+ * @param dx
+ * @param dy
+ * @param force
+ */
public void onTouchUp(
RemoteContext context,
CoreDocument document,
@@ -529,6 +590,15 @@
}
}
+ /**
+ * Touch Cancel handler
+ *
+ * @param context
+ * @param document
+ * @param x
+ * @param y
+ * @param force
+ */
public void onTouchCancel(
RemoteContext context, CoreDocument document, float x, float y, boolean force) {
if (!force && !contains(x, y)) {
@@ -551,6 +621,15 @@
}
}
+ /**
+ * Touch Drag handler
+ *
+ * @param context
+ * @param document
+ * @param x
+ * @param y
+ * @param force
+ */
public void onTouchDrag(
RemoteContext context, CoreDocument document, float x, float y, boolean force) {
if (!force && !contains(x, y)) {
@@ -573,6 +652,12 @@
}
}
+ /**
+ * Returns the location of the component relative to the root component
+ *
+ * @param value a 2 dimension float array that will receive the horizontal and vertical position
+ * of the component.
+ */
public void getLocationInWindow(@NonNull float[] value) {
value[0] += mX;
value[1] += mY;
@@ -681,6 +766,7 @@
}
}
+ /** Mark the tree as needing a repaint */
public void needsRepaint() {
try {
getRoot().mNeedsRepaint = true;
@@ -689,6 +775,11 @@
}
}
+ /**
+ * Debugging function returning the list of child operations
+ *
+ * @return a formatted string with the list of operations
+ */
@NonNull
public String content() {
StringBuilder builder = new StringBuilder();
@@ -700,6 +791,11 @@
return builder.toString();
}
+ /**
+ * Returns a string containing the text operations if any
+ *
+ * @return
+ */
@NonNull
public String textContent() {
StringBuilder builder = new StringBuilder();
@@ -713,6 +809,12 @@
return builder.toString();
}
+ /**
+ * Utility debug function
+ *
+ * @param component
+ * @param context
+ */
public void debugBox(@NonNull Component component, @NonNull PaintContext context) {
float width = component.mWidth;
float height = component.mHeight;
@@ -731,11 +833,22 @@
context.restorePaint();
}
+ /**
+ * Set the position of this component relative to its parent
+ *
+ * @param x horizontal position
+ * @param y vertical position
+ */
public void setLayoutPosition(float x, float y) {
this.mX = x;
this.mY = y;
}
+ /**
+ * The vertical position of this component relative to its parent
+ *
+ * @return
+ */
public float getTranslateX() {
if (mParent != null) {
return mX - mParent.mX;
@@ -743,6 +856,11 @@
return 0f;
}
+ /**
+ * The horizontal position of this component relative to its parent
+ *
+ * @return
+ */
public float getTranslateY() {
if (mParent != null) {
return mY - mParent.mY;
@@ -750,6 +868,11 @@
return 0f;
}
+ /**
+ * Paint the component itself.
+ *
+ * @param context
+ */
public void paintingComponent(@NonNull PaintContext context) {
if (mPreTranslate != null) {
mPreTranslate.paint(context);
@@ -778,6 +901,12 @@
context.getContext().mLastComponent = prev;
}
+ /**
+ * If animation is turned on and we need to be animated, we'll apply it.
+ *
+ * @param context
+ * @return
+ */
public boolean applyAnimationAsNeeded(@NonNull PaintContext context) {
if (context.isAnimationEnabled() && mAnimateMeasure != null) {
mAnimateMeasure.paint(context);
@@ -822,6 +951,11 @@
paintingComponent(context);
}
+ /**
+ * Extract child components
+ *
+ * @param components an ArrayList that will be populated by child components (if any)
+ */
public void getComponents(@NonNull ArrayList<Component> components) {
for (Operation op : mList) {
if (op instanceof Component) {
@@ -830,6 +964,11 @@
}
}
+ /**
+ * Extract child TextData elements
+ *
+ * @param data an ArrayList that will be populated with the TextData elements (if any)
+ */
public void getData(@NonNull ArrayList<TextData> data) {
for (Operation op : mList) {
if (op instanceof TextData) {
@@ -838,6 +977,11 @@
}
}
+ /**
+ * Returns the number of children components
+ *
+ * @return
+ */
public int getComponentCount() {
int count = 0;
for (Operation op : mList) {
@@ -848,6 +992,12 @@
return count;
}
+ /**
+ * Return the id used for painting the component -- either its component id or its animation id
+ * (if set)
+ *
+ * @return
+ */
public int getPaintId() {
if (mAnimationId != -1) {
return mAnimationId;
@@ -855,10 +1005,21 @@
return mComponentId;
}
+ /**
+ * Return true if the needsRepaint flag is set on this component
+ *
+ * @return
+ */
public boolean doesNeedsRepaint() {
return mNeedsRepaint;
}
+ /**
+ * Utility function to return a component from its id
+ *
+ * @param cid
+ * @return
+ */
@Nullable
public Component getComponent(int cid) {
if (mComponentId == cid || mAnimationId == cid) {
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 f009d88..5ef71d0 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
@@ -126,6 +126,12 @@
public static final int LAYOUT_ROW = 15;
public static final int LAYOUT_COLUMN = 16;
+ /**
+ * Returns the string representation of the component type
+ *
+ * @param type
+ * @return a string representing the component type
+ */
@NonNull
public static String typeDescription(int type) {
switch (type) {
@@ -179,6 +185,15 @@
return Operations.COMPONENT_START;
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param type
+ * @param componentId
+ * @param width
+ * @param height
+ */
public static void apply(
@NonNull WireBuffer buffer, int type, int componentId, float width, float height) {
buffer.start(Operations.COMPONENT_START);
@@ -188,6 +203,11 @@
buffer.writeFloat(height);
}
+ /**
+ * Return the size of the operation in byte
+ *
+ * @return the size in byte
+ */
public static int size() {
return 1 + 4 + 4 + 4;
}
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
index c678f6c..41e2bf8 100644
--- 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
@@ -21,7 +21,13 @@
import java.util.ArrayList;
+/** An Operation container */
public interface Container {
+ /**
+ * Returns a list of child operations
+ *
+ * @return a list of child operations
+ */
@NonNull
ArrayList<Operation> getList();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ContainerEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ContainerEnd.java
index 4290c4b..004f194 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ContainerEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ContainerEnd.java
@@ -68,6 +68,11 @@
return Operations.CONTAINER_END;
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ */
public static void apply(@NonNull WireBuffer buffer) {
buffer.start(id());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseOperation.java
new file mode 100644
index 0000000..e277d49
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseOperation.java
@@ -0,0 +1,231 @@
+/*
+ * 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.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a Impulse Event To trigger an impulse event. set the startAt time to the
+ * context.getAnimationTime() Impluse Operation. This operation execute a list of actions once and
+ * the impluseProcess is executed for a fixed duration
+ */
+public class ImpulseOperation extends PaintOperation implements VariableSupport, Container {
+ private static final int OP_CODE = Operations.IMPULSE_START;
+ private static final String CLASS_NAME = "ImpulseOperation";
+ private float mDuration;
+ private float mStartAt;
+ private float mOutDuration;
+ private float mOutStartAt;
+ private boolean mInitialPass = true;
+ @NonNull public ArrayList<Operation> mList = new ArrayList<>();
+
+ int mIndexVariableId;
+
+ private ImpulseProcess mProcess;
+
+ /**
+ * Constructor for a Impulse Operation
+ *
+ * @param duration the duration of the impluse
+ * @param startAt the start time of the impluse
+ */
+ public ImpulseOperation(float duration, float startAt) {
+ mDuration = duration;
+ mStartAt = startAt;
+ mOutStartAt = startAt;
+ mOutDuration = duration;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (mProcess == null) {
+ System.out.println(".....");
+ Operation last = mList.get(mList.size() - 1);
+ if (last instanceof ImpulseProcess) {
+ mProcess = (ImpulseProcess) last;
+ mList.remove(last);
+ }
+ }
+ if (Float.isNaN(mStartAt)) {
+ context.listensTo(Utils.idFromNan(mStartAt), this);
+ }
+ if (Float.isNaN(mDuration)) {
+ context.listensTo(Utils.idFromNan(mDuration), this);
+ }
+ for (Operation operation : mList) {
+ if (operation instanceof VariableSupport) {
+ VariableSupport variableSupport = (VariableSupport) operation;
+ variableSupport.registerListening(context);
+ }
+ }
+ if (mProcess != null) {
+ mProcess.registerListening(context);
+ }
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+
+ mOutDuration =
+ Float.isNaN(mDuration) ? context.getFloat(Utils.idFromNan(mDuration)) : mDuration;
+
+ mOutStartAt =
+ Float.isNaN(mStartAt) ? context.getFloat(Utils.idFromNan(mStartAt)) : mStartAt;
+ if (mProcess != null) {
+ mProcess.updateVariables(context);
+ }
+ }
+
+ @NonNull
+ public ArrayList<Operation> getList() {
+ return mList;
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mDuration, mStartAt);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("LoopOperation\n");
+ for (Operation operation : mList) {
+ builder.append(" startAt: ");
+ builder.append(mStartAt);
+ builder.append(" duration: ");
+ builder.append(mDuration);
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {
+ RemoteContext remoteContext = context.getContext();
+
+ if (remoteContext.getAnimationTime() < mOutStartAt + mOutDuration) {
+ if (mInitialPass) {
+ for (Operation op : mList) {
+ if (op instanceof VariableSupport && op.isDirty()) {
+ ((VariableSupport) op).updateVariables(context.getContext());
+ }
+ remoteContext.incrementOpCount();
+ op.apply(context.getContext());
+ }
+ mInitialPass = false;
+ } else {
+ remoteContext.incrementOpCount();
+ if (mProcess != null) {
+ mProcess.paint(context);
+ }
+ }
+ } else {
+ mInitialPass = true;
+ }
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param duration
+ * @param startAt
+ */
+ public static void apply(@NonNull WireBuffer buffer, float duration, float startAt) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(duration);
+ buffer.writeFloat(startAt);
+ }
+
+ /**
+ * 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) {
+ float duration = buffer.readFloat();
+ float startAt = buffer.readFloat();
+
+ operations.add(new ImpulseOperation(duration, startAt));
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Operations", OP_CODE, name())
+ .description(
+ "Impulse Operation. This operation execute a list of action for a fixed"
+ + " duration")
+ .field(DocumentedOperation.FLOAT, "duration", "How long to last")
+ .field(DocumentedOperation.FLOAT, "startAt", "value step");
+ }
+
+ /**
+ * Calculate and estimate of the number of iterations
+ *
+ * @return number of loops or 10 if based on variables
+ */
+ public int estimateIterations() {
+ if (Float.isNaN(mDuration)) {
+ return 10; // this is a generic estmate if the values are variables;
+ }
+ return (int) (mDuration * 60);
+ }
+
+ /**
+ * set the impulse process. This gets executed for the duration of the impulse
+ *
+ * @param impulseProcess process to be executed every time
+ */
+ public void setProcess(ImpulseProcess impulseProcess) {
+ mProcess = impulseProcess;
+ }
+}
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
new file mode 100644
index 0000000..f896f3d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseProcess.java
@@ -0,0 +1,154 @@
+/*
+ * 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.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Represents the repeating part of an Impulse. */
+public class ImpulseProcess extends PaintOperation implements VariableSupport, Container {
+ private static final int OP_CODE = Operations.IMPULSE_PROCESS;
+ private static final String CLASS_NAME = "ImpulseProcess";
+
+ @NonNull public ArrayList<Operation> mList = new ArrayList<>();
+
+ /** The constructor */
+ public ImpulseProcess() {}
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ for (Operation operation : mList) {
+ if (operation instanceof VariableSupport) {
+ VariableSupport variableSupport = (VariableSupport) operation;
+ variableSupport.registerListening(context);
+ }
+ }
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ for (Operation operation : mList) {
+ if (operation instanceof VariableSupport) {
+ VariableSupport variableSupport = (VariableSupport) operation;
+ variableSupport.updateVariables(context);
+ }
+ }
+ }
+
+ /**
+ * The returns a list to be filled
+ *
+ * @return list to be filled
+ */
+ @NonNull
+ public ArrayList<Operation> getList() {
+ return mList;
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(CLASS_NAME + "\n");
+ for (Operation operation : mList) {
+ builder.append(" ");
+ builder.append(operation);
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {
+ RemoteContext remoteContext = context.getContext();
+ for (Operation op : mList) {
+ if (op instanceof VariableSupport && op.isDirty()) {
+ ((VariableSupport) op).updateVariables(context.getContext());
+ }
+ remoteContext.incrementOpCount();
+ op.apply(context.getContext());
+ }
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return "Loop";
+ }
+
+ /**
+ * Apply this operation to the buffer
+ *
+ * @param buffer
+ */
+ public static void apply(@NonNull WireBuffer buffer) {
+ buffer.start(OP_CODE);
+ }
+
+ /**
+ * 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 ImpulseProcess());
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Operations", OP_CODE, name())
+ .description("Impulse Process that runs a list of operations");
+ }
+
+ /**
+ * Calculate and estimate of the number of iterations
+ *
+ * @return number of loops or 10 if based on variables
+ */
+ public int estimateIterations() {
+ return 1;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index 9103885..e71cb9a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -22,6 +22,7 @@
import com.android.internal.widget.remotecompose.core.OperationInterface;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.TouchListener;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.operations.BitmapData;
import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
@@ -176,8 +177,8 @@
|| (op instanceof PaintData)
|| (op instanceof FloatExpression)) {
supportedOperations.add(op);
- if (op instanceof TouchExpression) {
- ((TouchExpression) op).setComponent(this);
+ if (op instanceof TouchListener) {
+ ((TouchListener) op).setComponent(this);
}
} else {
// nothing
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 27172aa..4babe5f 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
@@ -66,6 +66,12 @@
return "CONTENT";
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param componentId
+ */
public static void apply(@NonNull WireBuffer buffer, int componentId) {
buffer.start(Operations.LAYOUT_CONTENT);
buffer.writeInt(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 6dce6f1..bfa417e8 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
@@ -88,6 +88,18 @@
}
}
+ /**
+ * Execute the list of actions
+ *
+ * @param context the RemoteContext
+ * @param document the current document
+ * @param component the component we belong to
+ * @param x the x touch down coordinate
+ * @param y the y touch down coordinate
+ * @param force if true, will apply the actions even if the component is not visible / not
+ * containing the touch down coordinates
+ * @return true if we applied the actions, false otherwise
+ */
public boolean applyActions(
RemoteContext context,
CoreDocument document,
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 f5954ee..0f4cf56 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
@@ -33,6 +33,7 @@
/** Represents a loop of operations */
public class LoopOperation extends PaintOperation implements Container, VariableSupport {
+
private static final int OP_CODE = Operations.LOOP_START;
@NonNull public ArrayList<Operation> mList = new ArrayList<>();
@@ -77,6 +78,7 @@
mIndexVariableId = indexId;
}
+ @Override
@NonNull
public ArrayList<Operation> getList() {
return mList;
@@ -139,6 +141,15 @@
return "Loop";
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param indexId
+ * @param from
+ * @param step
+ * @param until
+ */
public static void apply(
@NonNull WireBuffer buffer, int indexId, float from, float step, float until) {
buffer.start(OP_CODE);
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 baff5ee..f94cda2 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
@@ -174,6 +174,11 @@
context.restore();
}
+ /**
+ * Display the component hierarchy
+ *
+ * @return a formatted string containing the component hierarchy
+ */
@NonNull
public String displayHierarchy() {
StringSerializer serializer = new StringSerializer();
@@ -181,6 +186,13 @@
return serializer.toString();
}
+ /**
+ * Display the component hierarchy
+ *
+ * @param component the current component
+ * @param indent the current indentation level
+ * @param serializer the serializer we write to
+ */
public void displayHierarchy(
@NonNull Component component, int indent, @NonNull StringSerializer serializer) {
component.serializeToString(indent, serializer);
@@ -214,6 +226,12 @@
return Operations.LAYOUT_ROOT;
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer
+ * @param componentId
+ */
public static void apply(@NonNull WireBuffer buffer, int componentId) {
buffer.start(Operations.LAYOUT_ROOT);
buffer.writeInt(componentId);
@@ -249,6 +267,11 @@
apply(buffer, mComponentId);
}
+ /**
+ * Returns true if we have components with a touch listener
+ *
+ * @return true if listeners, false otherwise
+ */
public boolean hasTouchListeners() {
return mHasTouchListeners;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
index 842c9c1..96e29cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
@@ -40,6 +40,11 @@
}
}
+ /**
+ * Add a node to the current node
+ *
+ * @param node
+ */
public void add(@NonNull Node node) {
list.add(node);
}
@@ -54,23 +59,35 @@
@NonNull public static Node node = new Node(null, "Root");
@NonNull public static Node currentNode = node;
+ /** clear the current logging */
public static void clear() {
node = new Node(null, "Root");
currentNode = node;
}
+ /**
+ * start a node
+ *
+ * @param valueSupplier
+ */
public static void s(@NonNull StringValueSupplier valueSupplier) {
if (DEBUG_LAYOUT_ON) {
currentNode = new Node(currentNode, valueSupplier.getString());
}
}
+ /**
+ * arbitrary log statement
+ *
+ * @param valueSupplier
+ */
public static void log(@NonNull StringValueSupplier valueSupplier) {
if (DEBUG_LAYOUT_ON) {
new LogNode(currentNode, valueSupplier.getString());
}
}
+ /** end a node */
public static void e() {
if (DEBUG_LAYOUT_ON) {
if (currentNode.parent != null) {
@@ -81,6 +98,11 @@
}
}
+ /**
+ * end a node
+ *
+ * @param valueSupplier
+ */
public static void e(@NonNull StringValueSupplier valueSupplier) {
if (DEBUG_LAYOUT_ON) {
currentNode.endString = valueSupplier.getString();
@@ -92,6 +114,13 @@
}
}
+ /**
+ * print a given node
+ *
+ * @param indent
+ * @param node
+ * @param builder
+ */
public static void printNode(int indent, @NonNull Node node, @NonNull StringBuilder builder) {
if (DEBUG_LAYOUT_ON) {
StringBuilder indentationBuilder = new StringBuilder();
@@ -121,6 +150,7 @@
}
}
+ /** Output the captured log to System.out */
public static void display() {
if (DEBUG_LAYOUT_ON) {
StringBuilder builder = new StringBuilder();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java
index c7e2442..56a0410 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java
@@ -108,14 +108,14 @@
return mStage[i].getPos(t);
}
}
- var ret = (float) getEasing((t - mStage[lastStages].mStartTime));
+ float ret = (float) getEasing((t - mStage[lastStages].mStartTime));
ret += mStage[lastStages].mStartPos;
return ret;
}
@Override
public String toString() {
- var s = " ";
+ String s = " ";
for (int i = 0; i < mNumberOfStages; i++) {
Stage stage = mStage[i];
s += " $i $stage";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilityModifier.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilityModifier.java
index cd8b7b8..098c4c3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilityModifier.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibilityModifier.java
@@ -17,7 +17,20 @@
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
-/** A Modifier that provides semantic info. */
+/**
+ * A Modifier that provides semantic info.
+ *
+ * <p>This is needed since `AccessibilityModifier` is generally an open set and designed to be
+ * extended.
+ */
public interface AccessibilityModifier extends ModifierOperation, AccessibleComponent {
+ /**
+ * This method retrieves the operation code.
+ *
+ * <p>This function is used to get the current operation code associated with the object or
+ * context this method belongs to.
+ *
+ * @return The operation code as an integer.
+ */
int getOpCode();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
index e07fc4d..cc6c2a6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
@@ -17,29 +17,100 @@
import android.annotation.Nullable;
+/**
+ * Interface representing an accessible component in the UI. This interface defines properties and
+ * methods related to accessibility semantics for a component. It extends the {@link
+ * AccessibilitySemantics} interface to inherit semantic properties.
+ *
+ * <p>This is similar to {@link CoreSemantics} but handles built in operations that also expose
+ * those core semantics.
+ */
public interface AccessibleComponent extends AccessibilitySemantics {
+ /**
+ * Returns the ID of the content description for this item.
+ *
+ * <p>The content description is used to provide textual information about the item to
+ * accessibility services, such as screen readers. This allows users with visual impairments to
+ * understand the purpose and content of the item.
+ *
+ * <p>This is similar to AccessibilityNodeInfo.getContentDescription().
+ *
+ * @return The ID of a RemoteString for the content description, or null if no content
+ * description is provided.
+ */
default @Nullable Integer getContentDescriptionId() {
return null;
}
+ /**
+ * Gets the text ID associated with this object.
+ *
+ * <p>This method is intended to be overridden by subclasses that need to associate a specific
+ * text ID with themselves. The default implementation returns null, indicating that no text ID
+ * is associated with the object.
+ *
+ * <p>This is similar to AccessibilityNodeInfo.getText().
+ *
+ * @return The text ID, or null if no text ID is associated with this object.
+ */
default @Nullable Integer getTextId() {
return null;
}
+ /**
+ * Retrieves the role associated with this object. The enum type deliberately matches the
+ * Compose Role. In the platform it will be applied as ROLE_DESCRIPTION_KEY.
+ *
+ * <p>The default implementation returns {@code null}, indicating that no role is assigned.
+ *
+ * @return The role associated with this object, or {@code null} if no role is assigned.
+ */
default @Nullable Role getRole() {
return null;
}
+ /**
+ * Checks if the element is clickable.
+ *
+ * <p>By default, elements are not considered clickable. Subclasses should override this method
+ * to indicate clickability based on their specific properties and behavior.
+ *
+ * <p>This is similar to AccessibilityNodeInfo.isClickable().
+ *
+ * @return {@code true} if the element is clickable, {@code false} otherwise.
+ */
default boolean isClickable() {
return false;
}
+ /**
+ * Gets the merge mode of the operation.
+ *
+ * <p>The mode indicates the type of operation being performed. By default it returns {@link
+ * CoreSemantics.Mode#SET}, indicating a "set" operation.
+ *
+ * <p>{@link CoreSemantics.Mode#CLEAR_AND_SET} matches a Compose modifier of
+ * `Modifier.clearAndSetSemantics {}`
+ *
+ * <p>{@link CoreSemantics.Mode#MERGE} matches a Compose modifier of
+ * `Modifier.semantics(mergeDescendants = true) {}`
+ *
+ * @return The mode of the operation, which defaults to {@link CoreSemantics.Mode#SET}.
+ */
default CoreSemantics.Mode getMode() {
return CoreSemantics.Mode.SET;
}
- // Our master list
- // https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/Role
+ /**
+ * Represents the role of an accessible component.
+ *
+ * <p>The enum type deliberately matches the Compose Role. In the platform it will be applied as
+ * ROLE_DESCRIPTION_KEY.
+ *
+ * @link <a
+ * href="https://developer.android.com/reference/androidx/compose/ui/semantics/Role">Compose
+ * Semantics Role</a>
+ */
enum Role {
BUTTON("Button"),
CHECKBOX("Checkbox"),
@@ -70,4 +141,21 @@
return Role.UNKNOWN;
}
}
+
+ /**
+ * Defines the merge mode of an element in the semantic tree.
+ *
+ * <p>{@link CoreSemantics.Mode#CLEAR_AND_SET} matches a Compose modifier of
+ * `Modifier.clearAndSetSemantics {}`
+ *
+ * <p>{@link CoreSemantics.Mode#MERGE} matches a Compose modifier of
+ * `Modifier.semantics(mergeDescendants = true) {}`
+ *
+ * <p>{@link CoreSemantics.Mode#SET} adds or overrides semantics on an element.
+ */
+ enum Mode {
+ SET,
+ CLEAR_AND_SET,
+ MERGE
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/CoreSemantics.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/CoreSemantics.java
index 4047dd2..5b35ee5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/CoreSemantics.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/CoreSemantics.java
@@ -118,16 +118,22 @@
return indent + this;
}
- @NonNull
- public String serializedName() {
- return "SEMANTICS";
- }
-
@Override
public void serializeToString(int indent, @NonNull StringSerializer serializer) {
- serializer.append(indent, serializedName() + " = " + this);
+ serializer.append(indent, "SEMANTICS" + " = " + this);
}
+ /**
+ * Reads a CoreSemantics object from a WireBuffer and adds it to a list of operations.
+ *
+ * <p>This method reads the data required to construct a CoreSemantics object from the provided
+ * WireBuffer. After reading and constructing the CoreSemantics object, it is added to the
+ * provided list of operations.
+ *
+ * @param buffer The WireBuffer to read data from.
+ * @param operations The list of operations to which the read CoreSemantics object will be
+ * added.
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
CoreSemantics semantics = new CoreSemantics();
@@ -148,10 +154,4 @@
public @Nullable Integer getTextId() {
return mTextId != 0 ? mTextId : null;
}
-
- public enum Mode {
- SET,
- CLEAR_AND_SET,
- MERGE
- }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index da65a9c..e1a01a6 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
@@ -47,6 +47,7 @@
Point mActionDownPoint = new Point(0, 0);
AndroidRemoteContext mARContext = new AndroidRemoteContext();
float mDensity = 1f;
+ long mStart = System.nanoTime();
long mLastFrameDelay = 1;
float mMaxFrameRate = 60f; // frames per seconds
@@ -109,6 +110,7 @@
setContentDescription(mDocument.getDocument().getContentDescription());
updateClickAreas();
requestLayout();
+ mARContext.loadFloat(RemoteContext.ID_TOUCH_EVENT_TIME, -Float.MAX_VALUE);
invalidate();
}
@@ -337,6 +339,8 @@
mActionDownPoint.y = (int) event.getY();
CoreDocument doc = mDocument.getDocument();
if (doc.hasTouchListener()) {
+ mARContext.loadFloat(
+ RemoteContext.ID_TOUCH_EVENT_TIME, mARContext.getAnimationTime());
mInActionDown = true;
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
@@ -368,6 +372,8 @@
performClick();
doc = mDocument.getDocument();
if (doc.hasTouchListener()) {
+ mARContext.loadFloat(
+ RemoteContext.ID_TOUCH_EVENT_TIME, mARContext.getAnimationTime());
mVelocityTracker.computeCurrentVelocity(1000);
float dx = mVelocityTracker.getXVelocity(pointerId);
float dy = mVelocityTracker.getYVelocity(pointerId);
@@ -380,6 +386,8 @@
case MotionEvent.ACTION_MOVE:
if (mInActionDown) {
if (mVelocityTracker != null) {
+ mARContext.loadFloat(
+ RemoteContext.ID_TOUCH_EVENT_TIME, mARContext.getAnimationTime());
mVelocityTracker.addMovement(event);
doc = mDocument.getDocument();
boolean repaint = doc.touchDrag(mARContext, event.getX(), event.getY());
@@ -453,7 +461,8 @@
private int mCount;
private long mTime = System.nanoTime();
private long mDuration;
- private boolean mEvalTime = false;
+ private boolean mEvalTime = false; // turn on to measure eval time
+ private float mLastAnimationTime = 0.1f; // set to random non 0 number
/**
* This returns the amount of time in ms the player used to evalueate a pass it is averaged over
@@ -480,7 +489,18 @@
if (mDocument == null) {
return;
}
- long start = mEvalTime ? System.nanoTime() : 0;
+ long start = mEvalTime ? System.nanoTime() : 0; // measure execut of commands
+
+ float animationTime = (System.nanoTime() - mStart) * 1E-9f;
+ mARContext.setAnimationTime(animationTime);
+ mARContext.loadFloat(RemoteContext.ID_ANIMATION_TIME, animationTime);
+ mARContext.loadFloat(
+ RemoteContext.ID_ANIMATION_DELTA_TIME, animationTime - mLastAnimationTime);
+ mLastAnimationTime = animationTime;
+ mARContext.setAnimationEnabled(true);
+ mARContext.currentTime = System.currentTimeMillis();
+ mARContext.setDebug(mDebug);
+ float density = getContext().getResources().getDisplayMetrics().density;
mARContext.useCanvas(canvas);
mARContext.mWidth = getWidth();
mARContext.mHeight = getHeight();