diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java index a62e9d131d..ed81d7e16b 100644 --- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java @@ -21,8 +21,10 @@ import static android.content.Intent.EXTRA_USER; 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.Looper; 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 @@ import java.util.function.Consumer; public class FallbackSwipeHandler extends AbsSwipeUpHandler { + 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 @@ public class FallbackSwipeHandler extends } } - private class FallbackHomeAnimationFactory extends HomeAnimationFactory { + private class FallbackHomeAnimationFactory extends HomeAnimationFactory + implements Consumer { private final Rect mTempRect = new Rect(); private final TransformParams mHomeAlphaParams = new TransformParams(); private final AnimatedFloat mHomeAlpha; @@ -235,6 +242,9 @@ public class FallbackSwipeHandler extends 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 @@ public class FallbackSwipeHandler extends @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 @@ public class FallbackSwipeHandler extends if (mSpringAnim != null) { mSpringAnim.onTargetPositionChanged(); } + mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK); } + maybeSendEndMessage(); } catch (Exception e) { // Ignore } @@ -382,8 +411,8 @@ public class FallbackSwipeHandler extends 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 2a7e629247..c782dca4ca 100644 --- a/src/com/android/launcher3/GestureNavContract.java +++ b/src/com/android/launcher3/GestureNavContract.java @@ -18,20 +18,30 @@ package com.android.launcher3; 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 class GestureNavContract { 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 @@ public class GestureNavContract { * 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 @@ public class GestureNavContract { } 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 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 1cead11cbf..2636d55ae2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -604,7 +604,13 @@ public class Launcher extends StatefulActivity implements Launche 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 19c28b4c72..bfb75f0022 100644 --- a/src/com/android/launcher3/views/FloatingSurfaceView.java +++ b/src/com/android/launcher3/views/FloatingSurfaceView.java @@ -62,7 +62,6 @@ public class FloatingSurfaceView extends AbstractFloatingView implements private final SurfaceView mSurfaceView; - private View mIcon; private GestureNavContract mContract; @@ -103,7 +102,13 @@ public class FloatingSurfaceView extends AbstractFloatingView implements 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 @@ public class FloatingSurfaceView extends AbstractFloatingView implements 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 @@ public class FloatingSurfaceView extends AbstractFloatingView implements @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { close(false); + removeViewImmediate(); return false; } @@ -197,7 +201,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements private void sendIconInfo() { if (mContract != null && !mIconPosition.isEmpty()) { - mContract.sendEndPosition(mIconPosition, mSurfaceView.getSurfaceControl()); + mContract.sendEndPosition(mIconPosition, mLauncher, mSurfaceView.getSurfaceControl()); } }