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();