Adding finish signal to GestureNavContract.

Also fixing also-closing behavior for FloatingSurfaceView for Android S

Bug: 228260232
Test: Verified manually on device
Change-Id: I83f6417a756c1459533294d681e83abba6ea58aa
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index a62e9d1..ed81d7e 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -21,8 +21,10 @@
 import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT;
 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
+import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK;
 import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
 import static com.android.launcher3.Utilities.createHomeIntent;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
@@ -44,7 +46,9 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.ParcelUuid;
+import android.os.RemoteException;
 import android.os.UserHandle;
+import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
@@ -82,6 +86,8 @@
 public class FallbackSwipeHandler extends
         AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView, RecentsState> {
 
+    private static final String TAG = "FallbackSwipeHandler";
+
     /**
      * Message used for receiving gesture nav contract information. We use a static messenger to
      * avoid leaking too make binders in case the receiving launcher does not handle the contract
@@ -224,7 +230,8 @@
         }
     }
 
-    private class FallbackHomeAnimationFactory extends HomeAnimationFactory {
+    private class FallbackHomeAnimationFactory extends HomeAnimationFactory
+            implements Consumer<Message> {
         private final Rect mTempRect = new Rect();
         private final TransformParams mHomeAlphaParams = new TransformParams();
         private final AnimatedFloat mHomeAlpha;
@@ -235,6 +242,9 @@
         private final RectF mTargetRect = new RectF();
         private SurfaceControl mSurfaceControl;
 
+        private boolean mAnimationFinished;
+        private Message mOnFinishCallback;
+
         private final long mDuration;
 
         private RectFSpringAnim mSpringAnim;
@@ -336,9 +346,26 @@
         @Override
         public void setAnimation(RectFSpringAnim anim) {
             mSpringAnim = anim;
+            mSpringAnim.addAnimatorListener(forEndCallback(this::onRectAnimationEnd));
         }
 
-        private void onMessageReceived(Message msg) {
+        private void onRectAnimationEnd() {
+            mAnimationFinished = true;
+            maybeSendEndMessage();
+        }
+
+        private void maybeSendEndMessage() {
+            if (mAnimationFinished && mOnFinishCallback != null) {
+                try {
+                    mOnFinishCallback.replyTo.send(mOnFinishCallback);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error sending icon position", e);
+                }
+            }
+        }
+
+        @Override
+        public void accept(Message msg) {
             try {
                 Bundle data = msg.getData();
                 RectF position = data.getParcelable(EXTRA_ICON_POSITION);
@@ -348,7 +375,9 @@
                     if (mSpringAnim != null) {
                         mSpringAnim.onTargetPositionChanged();
                     }
+                    mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK);
                 }
+                maybeSendEndMessage();
             } catch (Exception e) {
                 // Ignore
             }
@@ -382,8 +411,8 @@
                 Bundle gestureNavContract = new Bundle();
                 gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent());
                 gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId));
-                gestureNavContract.putParcelable(EXTRA_REMOTE_CALLBACK,
-                        sMessageReceiver.newCallback(this::onMessageReceived));
+                gestureNavContract.putParcelable(
+                        EXTRA_REMOTE_CALLBACK, sMessageReceiver.newCallback(this));
                 intent.putExtra(EXTRA_GESTURE_CONTRACT, gestureNavContract);
             }
         }
diff --git a/src/com/android/launcher3/GestureNavContract.java b/src/com/android/launcher3/GestureNavContract.java
index 2a7e629..c782dca 100644
--- a/src/com/android/launcher3/GestureNavContract.java
+++ b/src/com/android/launcher3/GestureNavContract.java
@@ -18,20 +18,30 @@
 import static android.content.Intent.EXTRA_COMPONENT_NAME;
 import static android.content.Intent.EXTRA_USER;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
+
 import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.RectF;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.SurfaceControl;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.views.ActivityContext;
+
+import java.lang.ref.WeakReference;
+
 /**
  * Class to encapsulate the handshake protocol between Launcher and gestureNav.
  */
@@ -43,6 +53,7 @@
     public static final String EXTRA_ICON_POSITION = "gesture_nav_contract_icon_position";
     public static final String EXTRA_ICON_SURFACE = "gesture_nav_contract_surface_control";
     public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
+    public static final String EXTRA_ON_FINISH_CALLBACK = "gesture_nav_contract_finish_callback";
 
     public final ComponentName componentName;
     public final UserHandle user;
@@ -59,10 +70,15 @@
      * Sends the position information to the receiver
      */
     @TargetApi(Build.VERSION_CODES.R)
-    public void sendEndPosition(RectF position, @Nullable SurfaceControl surfaceControl) {
+    public void sendEndPosition(RectF position, ActivityContext context,
+            @Nullable SurfaceControl surfaceControl) {
         Bundle result = new Bundle();
         result.putParcelable(EXTRA_ICON_POSITION, position);
         result.putParcelable(EXTRA_ICON_SURFACE, surfaceControl);
+        if (sMessageReceiver == null) {
+            sMessageReceiver = new StaticMessageReceiver();
+        }
+        result.putParcelable(EXTRA_ON_FINISH_CALLBACK, sMessageReceiver.setCurrentContext(context));
 
         Message callback = Message.obtain();
         callback.copyFrom(mCallback);
@@ -98,4 +114,42 @@
         }
         return null;
     }
+
+    /**
+     * Message used for receiving gesture nav contract information. We use a static messenger to
+     * avoid leaking too make binders in case the receiving launcher does not handle the contract
+     * properly.
+     */
+    private static StaticMessageReceiver sMessageReceiver = null;
+
+    private static class StaticMessageReceiver implements Handler.Callback {
+
+        private static final int MSG_CLOSE_LAST_TARGET = 0;
+
+        private final Messenger mMessenger =
+                new Messenger(new Handler(Looper.getMainLooper(), this));
+
+        private WeakReference<ActivityContext> mLastTarget = new WeakReference<>(null);
+
+        public Message setCurrentContext(ActivityContext context) {
+            mLastTarget = new WeakReference<>(context);
+
+            Message msg = Message.obtain();
+            msg.replyTo = mMessenger;
+            msg.what = MSG_CLOSE_LAST_TARGET;
+            return msg;
+        }
+
+        @Override
+        public boolean handleMessage(@NonNull Message message) {
+            if (message.what == MSG_CLOSE_LAST_TARGET) {
+                ActivityContext lastContext = mLastTarget.get();
+                if (lastContext != null) {
+                    AbstractFloatingView.closeOpenViews(lastContext, false, TYPE_ICON_SURFACE);
+                }
+                return true;
+            }
+            return false;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1cead11..2636d55 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -604,7 +604,13 @@
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
         mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
-        AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE);
+        // Starting with Android S, onEnterAnimationComplete is sent immediately
+        // causing the surface to get removed before the animation completed (b/175345344).
+        // Instead we rely on next user touch event to remove the view and optionally a callback
+        // from system from Android T onwards.
+        if (!Utilities.ATLEAST_S) {
+            AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index 19c28b4..bfb75f0 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -62,7 +62,6 @@
 
     private final SurfaceView mSurfaceView;
 
-
     private View mIcon;
     private GestureNavContract mContract;
 
@@ -103,7 +102,13 @@
     private void removeViewFromParent() {
         mPicture.beginRecording(1, 1);
         mPicture.endRecording();
-        mLauncher.getDragLayer().removeView(this);
+        mLauncher.getDragLayer().removeViewInLayout(this);
+    }
+
+    private void removeViewImmediate() {
+        // Cancel any pending remove
+        Executors.MAIN_EXECUTOR.getHandler().removeCallbacks(mRemoveViewRunnable);
+        removeViewFromParent();
     }
 
     /**
@@ -115,9 +120,7 @@
         view.mContract = contract;
         view.mIsOpen = true;
 
-        // Cancel any pending remove
-        Executors.MAIN_EXECUTOR.getHandler().removeCallbacks(view.mRemoveViewRunnable);
-        view.removeViewFromParent();
+        view.removeViewImmediate();
         launcher.getDragLayer().addView(view);
     }
 
@@ -129,6 +132,7 @@
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         close(false);
+        removeViewImmediate();
         return false;
     }
 
@@ -197,7 +201,7 @@
 
     private void sendIconInfo() {
         if (mContract != null && !mIconPosition.isEmpty()) {
-            mContract.sendEndPosition(mIconPosition, mSurfaceView.getSurfaceControl());
+            mContract.sendEndPosition(mIconPosition, mLauncher, mSurfaceView.getSurfaceControl());
         }
     }