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/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());
         }
     }