Merge "Integrate with RemoteComposePlayer" into main
diff --git a/core/java/android/widget/RemoteCanvas.java b/core/java/android/widget/RemoteCanvas.java
deleted file mode 100644
index 9a0898c..0000000
--- a/core/java/android/widget/RemoteCanvas.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.widget;
-
-import static android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL;
-
-import android.annotation.AttrRes;
-import android.annotation.FlaggedApi;
-import android.annotation.StyleRes;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import java.util.function.IntConsumer;
-
-/**
- * {@link RemoteCanvas} is designed to support arbitrary protocols between two processes using
- * {@link RemoteViews.DrawInstructions}. Upon instantiation in the host process,
- * {@link RemoteCanvas#setDrawInstructions(RemoteViews.DrawInstructions)} is called so that the
- * host process can render the {@link RemoteViews.DrawInstructions} from the provider process
- * accordingly.
- *
- * @hide
- */
-@FlaggedApi(FLAG_DRAW_DATA_PARCEL)
-public class RemoteCanvas extends View {
-
-    private static final String TAG = "RemoteCanvas";
-
-    @Nullable
-    private SparseArray<Runnable> mCallbacks;
-
-    private final IntConsumer mOnClickHandler = (viewId) -> {
-        if (mCallbacks == null) {
-            Log.w(TAG, "Cannot find callback for " + viewId
-                    + ", in fact there were no callbacks from this RemoteViews at all.");
-            return;
-        }
-        final Runnable cb = getCallbacks().get(viewId);
-        if (cb != null) {
-            cb.run();
-        } else {
-            Log.w(TAG, "Cannot find callback for " + viewId);
-        }
-    };
-
-    RemoteCanvas(@NonNull Context context) {
-        super(context);
-    }
-
-    RemoteCanvas(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    RemoteCanvas(@NonNull Context context, @Nullable AttributeSet attrs,
-                 @AttrRes int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    RemoteCanvas(@NonNull Context context, @Nullable AttributeSet attrs,
-                 @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    /**
-     * Setter method for the {@link RemoteViews.DrawInstructions} from the provider process for
-     * the host process to render accordingly.
-     *
-     * @param instructions {@link RemoteViews.DrawInstructions} from the provider process.
-     */
-    void setDrawInstructions(@NonNull final RemoteViews.DrawInstructions instructions) {
-        setTag(instructions);
-        // TODO: handle draw instructions
-        // TODO: attach mOnClickHandler
-    }
-
-    /**
-     * Adds a callback function to a clickable area in the RemoteCanvas.
-     *
-     * @param viewId the viewId of the clickable area
-     * @param cb the callback function to be triggered when clicked
-     */
-    void addOnClickHandler(final int viewId, @NonNull final Runnable cb) {
-        getCallbacks().set(viewId, cb);
-    }
-
-    /**
-     * Returns all callbacks added to the RemoteCanvas through
-     * {@link #addOnClickHandler(int, Runnable)}.
-     */
-    @VisibleForTesting
-    public SparseArray<Runnable> getCallbacks() {
-        if (mCallbacks == null) {
-            mCallbacks = new SparseArray<>();
-        }
-        return mCallbacks;
-    }
-}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 738bb1f..5bf1b5b 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -112,7 +112,10 @@
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.internal.widget.remotecompose.player.RemoteComposeDocument;
+import com.android.internal.widget.remotecompose.player.RemoteComposePlayer;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
@@ -1479,9 +1482,7 @@
 
         @Override
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
-            if (hasDrawInstructions() && root instanceof RemoteCanvas target) {
-                target.addOnClickHandler(mViewId, () ->
-                        mResponse.handleViewInteraction(root, params.handler));
+            if (hasDrawInstructions() && root instanceof RemoteComposePlayer) {
                 return;
             }
             final View target = root.findViewById(mViewId);
@@ -3900,8 +3901,17 @@
         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException {
             if (drawDataParcel() && mInstructions != null
-                    && root instanceof RemoteCanvas remoteCanvas) {
-                remoteCanvas.setDrawInstructions(mInstructions);
+                    && root instanceof RemoteComposePlayer player) {
+                player.setTag(mInstructions);
+                final List<byte[]> bytes = mInstructions.mInstructions;
+                if (bytes.isEmpty()) {
+                    return;
+                }
+                try (ByteArrayInputStream is = new ByteArrayInputStream(bytes.get(0))) {
+                    player.setDocument(new RemoteComposeDocument(is));
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, "Failed to render draw instructions", e);
+                }
             }
         }
 
@@ -6041,6 +6051,16 @@
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
         View result = inflateView(context, rvToApply, directParent,
                 params.applyThemeResId, params.colorResources);
+        if (result instanceof RemoteComposePlayer player) {
+            player.addClickListener((viewId, metadata) -> {
+                mActions.forEach(action -> {
+                    if (viewId == action.mViewId
+                            && action instanceof SetOnClickResponse setOnClickResponse) {
+                        setOnClickResponse.mResponse.handleViewInteraction(player, params.handler);
+                    }
+                });
+            });
+        }
         rvToApply.performApply(result, rootParent, params);
         return result;
     }
@@ -6064,7 +6084,7 @@
         }
         // If the RemoteViews contains draw instructions, just use it instead.
         if (rv.hasDrawInstructions()) {
-            return new RemoteCanvas(inflationContext);
+            return new RemoteComposePlayer(inflationContext);
         }
         LayoutInflater inflater = LayoutInflater.from(context);
 
@@ -7546,7 +7566,7 @@
     public static final class DrawInstructions {
 
         @NonNull
-        private final List<byte[]> mInstructions;
+        final List<byte[]> mInstructions;
 
         private DrawInstructions() {
             throw new UnsupportedOperationException(
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 5862711..3d5494d 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -422,19 +422,9 @@
         if (!drawDataParcel()) {
             return;
         }
-        final RemoteViews.DrawInstructions drawInstructions = getDrawInstructions();
-        final RemoteViews rv = new RemoteViews(drawInstructions);
-        final View view = rv.apply(mContext, mContainer);
-        assertTrue(view instanceof RemoteCanvas);
-        assertEquals(drawInstructions, view.getTag());
-    }
-
-    @Test
-    public void remoteCanvasWiresClickHandlers() {
-        if (!drawDataParcel()) {
-            return;
-        }
-        final RemoteViews.DrawInstructions drawInstructions = getDrawInstructions();
+        final byte[] bytes = new byte[] {'h', 'e', 'l', 'l', 'o'};
+        final RemoteViews.DrawInstructions drawInstructions =
+                new RemoteViews.DrawInstructions.Builder(Collections.singletonList(bytes)).build();
         final RemoteViews rv = new RemoteViews(drawInstructions);
         final PendingIntent pi = PendingIntent.getActivity(mContext, 0,
                 new Intent(Intent.ACTION_VIEW), PendingIntent.FLAG_IMMUTABLE);
@@ -443,15 +433,7 @@
         rv.setPendingIntentTemplate(viewId, pi);
         rv.setOnClickFillInIntent(viewId, i);
         final View view = rv.apply(mContext, mContainer);
-        assertTrue(view instanceof RemoteCanvas);
-        RemoteCanvas target = (RemoteCanvas) view;
-        assertEquals(1, target.getCallbacks().size());
-        assertNotNull(target.getCallbacks().get(viewId));
-    }
-
-    private RemoteViews.DrawInstructions getDrawInstructions() {
-        final byte[] bytes = new byte[] {'h', 'e', 'l', 'l', 'o'};
-        return new RemoteViews.DrawInstructions.Builder(Collections.singletonList(bytes)).build();
+        assertEquals(drawInstructions, view.getTag());
     }
 
     private RemoteViews createViewChained(int depth, String... texts) {