Updating SurfaceTransactionApplier to handle view not being attached

Bug: 278833389
Test: Presubmit
Flag: N/A
Change-Id: I88c240a1a7bdfe2e68c858c30e91cc5d06bc7bcf
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5333cbe..7d47945 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -2114,12 +2114,11 @@
     }
 
     protected void linkRecentsViewScroll() {
-        SurfaceTransactionApplier.create(mRecentsView, applier -> {
-            runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
-                            .setSyncTransactionApplier(applier));
-            runOnRecentsAnimationAndLauncherBound(() ->
-                    mRecentsAnimationTargets.addReleaseCheck(applier));
-        });
+        SurfaceTransactionApplier applier = new SurfaceTransactionApplier(mRecentsView);
+        runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
+                        .setSyncTransactionApplier(applier));
+        runOnRecentsAnimationAndLauncherBound(() ->
+                mRecentsAnimationTargets.addReleaseCheck(applier));
 
         mRecentsView.addOnScrollChangedListener(mOnRecentsScrollListener);
         runOnRecentsAnimationAndLauncherBound(() ->
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 95473dc..bb028a7 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -22,13 +22,11 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewRootImpl;
 
 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
 
-import java.util.function.Consumer;
-
-
 /**
  * Helper class to apply surface transactions in sync with RenderThread similar to
  *   android.view.SyncRtSurfaceTransactionApplier
@@ -39,22 +37,47 @@
 
     private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
 
-    private final SurfaceControl mBarrierSurfaceControl;
-    private final ViewRootImpl mTargetViewRootImpl;
     private final Handler mApplyHandler;
 
+    private boolean mInitialized;
+    private SurfaceControl mBarrierSurfaceControl;
+    private ViewRootImpl mTargetViewRootImpl;
+
     private int mLastSequenceNumber = 0;
 
     /**
      * @param targetView The view in the surface that acts as synchronization anchor.
      */
     public SurfaceTransactionApplier(View targetView) {
-        mTargetViewRootImpl = targetView.getViewRootImpl();
-        mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl();
+        if (targetView.isAttachedToWindow()) {
+            initialize(targetView);
+        } else {
+            mInitialized = false;
+            targetView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    if (!mInitialized) {
+                        targetView.removeOnAttachStateChangeListener(this);
+                        initialize(targetView);
+                    }
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    // Do nothing
+                }
+            });
+        }
         mApplyHandler = new Handler(this::onApplyMessage);
         setCanRelease(true);
     }
 
+    private void initialize(View view) {
+        mTargetViewRootImpl = view.getViewRootImpl();
+        mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl();
+        mInitialized = true;
+    }
+
     protected boolean onApplyMessage(Message msg) {
         if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) {
             setCanRelease(msg.arg1 == mLastSequenceNumber);
@@ -70,6 +93,10 @@
      *               this method to avoid synchronization issues.
      */
     public void scheduleApply(SurfaceTransaction params) {
+        if (!mInitialized) {
+            params.getTransaction().apply();
+            return;
+        }
         View view = mTargetViewRootImpl.getView();
         if (view == null) {
             return;
@@ -93,33 +120,4 @@
         // Make sure a frame gets scheduled.
         view.invalidate();
     }
-
-    /**
-     * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
-     * attached if necessary.
-     */
-    public static void create(
-            final View targetView, final Consumer<SurfaceTransactionApplier> callback) {
-        if (targetView == null) {
-            // No target view, no applier
-            callback.accept(null);
-        } else if (targetView.isAttachedToWindow()) {
-            // Already attached, we're good to go
-            callback.accept(new SurfaceTransactionApplier(targetView));
-        } else {
-            // Haven't been attached before we can get the view root
-            targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
-                @Override
-                public void onViewAttachedToWindow(View v) {
-                    targetView.removeOnAttachStateChangeListener(this);
-                    callback.accept(new SurfaceTransactionApplier(targetView));
-                }
-
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                    // Do nothing
-                }
-            });
-        }
-    }
 }