Add live tile overlay only once across multiple LauncherSwipeHandler

- Use singleton on LiveTileOverlay to make sure we only have one LiveTileOverlay instance
- Clean up upon recents animation cancelation

Fixes: 140337263
Test: diligently swipe up and down
Change-Id: I8e0db3c5240135a6deb4f4d91493b8eede7c5ff1
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index c5cded0..50218d3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -176,8 +176,6 @@
     private boolean mHasLauncherTransitionControllerStarted;
 
     private AnimationFactory mAnimationFactory = (t) -> { };
-    private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
-    private boolean mLiveTileOverlayAttached = false;
 
     private boolean mWasLauncherAlreadyVisible;
 
@@ -547,7 +545,8 @@
 
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mRecentsAnimationTargets != null) {
-                mLiveTileOverlay.update(mAppWindowAnimationHelper.getCurrentRectWithInsets(),
+                LiveTileOverlay.getInstance().update(
+                        mAppWindowAnimationHelper.getCurrentRectWithInsets(),
                         mAppWindowAnimationHelper.getCurrentCornerRadius());
             }
         }
@@ -837,7 +836,7 @@
             setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
             duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
         } else if (endTarget == RECENTS) {
-            mLiveTileOverlay.startIconAnimation();
+            LiveTileOverlay.getInstance().startIconAnimation();
             if (mRecentsView != null) {
                 int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
                 if (mRecentsView.getNextPage() != nearestPage) {
@@ -1024,6 +1023,7 @@
             // In the off chance that the gesture ends before Launcher is started, we should clear
             // the callback here so that it doesn't update with the wrong state
             mActivity.clearRunOnceOnStartCallback();
+            resetLauncherListenersAndOverlays();
         }
         if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
             cancelCurrentAnimation();
@@ -1111,13 +1111,7 @@
         endLauncherTransitionController();
 
         mRecentsView.onGestureAnimationEnd();
-
-        // Reset the callback for deferred activity launches
-        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mActivityInterface.setOnDeferredActivityLaunchCallback(null);
-        }
-        mActivity.getRootView().setOnApplyWindowInsetsListener(null);
-        removeLiveTileOverlay();
+        resetLauncherListenersAndOverlays();
     }
 
     private void endLauncherTransitionController() {
@@ -1128,6 +1122,15 @@
         }
     }
 
+    private void resetLauncherListenersAndOverlays() {
+        // Reset the callback for deferred activity launches
+        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mActivityInterface.setOnDeferredActivityLaunchCallback(null);
+        }
+        mActivity.getRootView().setOnApplyWindowInsetsListener(null);
+        removeLiveTileOverlay();
+    }
+
     private void notifyTransitionCancelled() {
         mAnimationFactory.onTransitionCancelled();
     }
@@ -1232,20 +1235,15 @@
         updateFinalShift();
     }
 
-    private synchronized void addLiveTileOverlay() {
-        if (!mLiveTileOverlayAttached) {
-            mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
-            mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
-            mLiveTileOverlayAttached = true;
+    private void addLiveTileOverlay() {
+        if (LiveTileOverlay.getInstance().attach(mActivity.getRootView().getOverlay())) {
+            mRecentsView.setLiveTileOverlayAttached(true);
         }
     }
 
-    private synchronized void removeLiveTileOverlay() {
-        if (mLiveTileOverlayAttached) {
-            mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
-            mRecentsView.setLiveTileOverlay(null);
-            mLiveTileOverlayAttached = false;
-        }
+    private void removeLiveTileOverlay() {
+        LiveTileOverlay.getInstance().detach(mActivity.getRootView().getOverlay());
+        mRecentsView.setLiveTileOverlayAttached(false);
     }
 
     public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
index a838797..18eda60 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -16,6 +16,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.util.FloatProperty;
+import android.view.ViewOverlay;
 
 import com.android.launcher3.anim.Interpolators;
 
@@ -36,6 +37,15 @@
                 }
             };
 
+    private static LiveTileOverlay sInstance;
+
+    public static LiveTileOverlay getInstance() {
+        if (sInstance == null) {
+            sInstance = new LiveTileOverlay();
+        }
+        return sInstance;
+    }
+
     private final Paint mPaint = new Paint();
 
     private Rect mBoundsRect = new Rect();
@@ -46,8 +56,9 @@
 
     private boolean mDrawEnabled = true;
     private float mIconAnimationProgress = 0f;
+    private boolean mIsAttached;
 
-    public LiveTileOverlay() {
+    private LiveTileOverlay() {
         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
     }
 
@@ -124,6 +135,23 @@
         return PixelFormat.TRANSLUCENT;
     }
 
+    public boolean attach(ViewOverlay overlay) {
+        if (overlay != null && !mIsAttached) {
+            overlay.add(this);
+            mIsAttached = true;
+            return true;
+        }
+
+        return false;
+    }
+
+    public void detach(ViewOverlay overlay) {
+        if (overlay != null) {
+            overlay.remove(this);
+            mIsAttached = false;
+        }
+    }
+
     private void setIconAnimationProgress(float progress) {
         mIconAnimationProgress = progress;
         invalidateSelf();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index c055db1..56c2dd5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -308,7 +308,7 @@
     private final int mEmptyMessagePadding;
     private boolean mShowEmptyMessage;
     private Layout mEmptyTextLayout;
-    private LiveTileOverlay mLiveTileOverlay;
+    private boolean mLiveTileOverlayAttached;
 
     // Keeps track of the index where the first TaskView should be
     private int mTaskViewStartIndex = 0;
@@ -876,8 +876,8 @@
      */
     public void onSwipeUpAnimationSuccess() {
         if (getRunningTaskView() != null) {
-            float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlay != null
-                    ? mLiveTileOverlay.cancelIconAnimation()
+            float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlayAttached
+                    ? LiveTileOverlay.getInstance().cancelIconAnimation()
                     : 0f;
             animateUpRunningTaskIconScale(startProgress);
         }
@@ -1724,13 +1724,13 @@
         mAppWindowAnimationHelper = appWindowAnimationHelper;
     }
 
-    public void setLiveTileOverlay(LiveTileOverlay liveTileOverlay) {
-        mLiveTileOverlay = liveTileOverlay;
+    public void setLiveTileOverlayAttached(boolean liveTileOverlayAttached) {
+        mLiveTileOverlayAttached = liveTileOverlayAttached;
     }
 
     public void updateLiveTileIcon(Drawable icon) {
-        if (mLiveTileOverlay != null) {
-            mLiveTileOverlay.setIcon(icon);
+        if (mLiveTileOverlayAttached) {
+            LiveTileOverlay.getInstance().setIcon(icon);
         }
     }