Call close for AbstractFloatingViews that were removed while still open.

I'm not sure how/when this case occurs (perhaps during some transition/state
change), but manually removing the floating view matches the symptoms in the
bug.

Bug: 72996404
Change-Id: I1e7c1a338fcd16c8e07b3c49fb9c9b2097eb2708
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 693cf31..097c341 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -97,6 +97,7 @@
         handleClose(animate);
         BaseActivity.fromContext(getContext()).getUserEventDispatcher()
                 .resetElapsedContainerMillis("container closed");
+        mIsOpen = false;
     }
 
     protected abstract void handleClose(boolean animate);
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index 4eac4a4..e7ca121 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -24,7 +24,8 @@
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.TraceHelper;
+
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 
 /*
  *  This is a helper class that listens to updates from the corresponding animation.
@@ -36,7 +37,6 @@
     private static final String TAG = "FirstFrameAnimatorHlpr";
     private static final boolean DEBUG = false;
     private static final int MAX_DELAY = 1000;
-    private static final int IDEAL_FRAME_DURATION = 16;
     private final View mTarget;
     private long mStartFrame;
     private long mStartTime = -1;
@@ -109,9 +109,9 @@
             // prevents a large jump in the animation due to an expensive first frame
             } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
                        !mAdjustedSecondFrameTime &&
-                       currentTime > mStartTime + IDEAL_FRAME_DURATION &&
-                       currentPlayTime > IDEAL_FRAME_DURATION) {
-                animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
+                       currentTime > mStartTime + SINGLE_FRAME_MS &&
+                       currentPlayTime > SINGLE_FRAME_MS) {
+                animation.setCurrentPlayTime(SINGLE_FRAME_MS);
                 mAdjustedSecondFrameTime = true;
             } else {
                 if (frameNum > 1) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 98440ff..006dc95 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -103,6 +103,8 @@
     public static final boolean ATLEAST_LOLLIPOP_MR1 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
 
+    public static final int SINGLE_FRAME_MS = 16;
+
     /**
      * Indicates if the device has a debug build. Should only be used to store additional info or
      * add extra logging and not for changing the app behavior.
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 12d7dc7..99c800d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -39,6 +39,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
@@ -507,16 +508,9 @@
         // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
         mDeleteFolderOnDropCompleted = false;
 
-        final Runnable onCompleteRunnable;
         centerAboutIcon();
 
         AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator();
-        onCompleteRunnable = new Runnable() {
-            @Override
-            public void run() {
-                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
-            }
-        };
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
@@ -532,7 +526,7 @@
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
 
-                onCompleteRunnable.run();
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
                 mContent.setFocusOnFirstChild();
             }
         });
@@ -614,9 +608,6 @@
             mFolderIcon.clearLeaveBehindIfExists();
         }
 
-        if (!(getParent() instanceof DragLayer)) return;
-        DragLayer parent = (DragLayer) getParent();
-
         if (animate) {
             animateClosed();
         } else {
@@ -625,7 +616,8 @@
 
         // Notify the accessibility manager that this folder "window" has disappeared and no
         // longer occludes the workspace items
-        parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+        mLauncher.getDragLayer().sendAccessibilityEvent(
+                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
     private void animateClosed() {
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 4c7ce1f..c0ad110 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.touch;
 
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 
 import android.animation.Animator;
@@ -40,7 +41,6 @@
 
     private static final String TAG = "ASCTouchController";
     public static final float RECATCH_REJECTION_FRACTION = .0875f;
-    public static final int SINGLE_FRAME_MS = 16;
 
     // Progress after which the transition is assumed to be a success in case user does not fling
     public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 489e59e..149b38b 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -34,6 +34,8 @@
 
 import java.util.ArrayList;
 
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+
 /**
  * A viewgroup with utility methods for drag-n-drop and touch interception
  */
@@ -119,6 +121,21 @@
     }
 
     @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        if (child instanceof AbstractFloatingView) {
+            // Handles the case where the view is removed without being properly closed.
+            // This can happen if something goes wrong during a state change/transition.
+            postDelayed(() -> {
+                AbstractFloatingView floatingView = (AbstractFloatingView) child;
+                if (floatingView.isOpen()) {
+                    floatingView.close(false);
+                }
+            }, SINGLE_FRAME_MS);
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {