From 32f91ab991dd2a1c6c0054c86b8700f2b966fd36 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 28 Jun 2018 15:52:35 -0700 Subject: [PATCH] Proxying touch events from RecentsTouchConsumer to Launcher While swipe-up animation is running, the user can quickly start another touch gesture. In that case we keep the recents transtion active and proxy all touch events to launcher. Bug: 110901700 Change-Id: Ie3b448dfea00473082dc9143423d3596504a3fcc --- .../PortraitStatesTouchController.java | 3 +- .../uioverrides/TaskViewTouchController.java | 3 +- .../android/quickstep/LongSwipeHelper.java | 8 -- .../quickstep/RecentsAnimationWrapper.java | 110 ++++++++++++++++-- .../com/android/quickstep/TouchConsumer.java | 2 + .../quickstep/TouchInteractionService.java | 67 ++++++----- .../WindowTransformSwipeHandler.java | 39 +++++-- .../launcher3/AbstractFloatingView.java | 3 +- 8 files changed, 177 insertions(+), 58 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java index 0eead88832..1d1b7dac96 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -109,7 +110,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return false; } } - if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { + if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE) != null) { return false; } return true; diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java index cfd41191fa..9a920c843e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; @@ -79,7 +80,7 @@ public abstract class TaskViewTouchController // If we are already animating from a previous state, we can intercept. return true; } - if (AbstractFloatingView.getTopOpenView(mActivity) != null) { + if (AbstractFloatingView.getTopOpenViewWithType(mActivity, TYPE_ACCESSIBLE) != null) { return false; } return isRecentsInteractive(); diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java index 2fe7a11845..16214dd6ea 100644 --- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java +++ b/quickstep/src/com/android/quickstep/LongSwipeHelper.java @@ -169,12 +169,4 @@ public class LongSwipeHelper { callback.run(); } - - public float getTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) { - if (!(app.isNotInRecents - || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) { - return 0; - } - return expectedAlpha; - } } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java index b0313fc2bb..eea3971f40 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -15,14 +15,23 @@ */ package com.android.quickstep; +import static android.view.MotionEvent.ACTION_CANCEL; +import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_UP; + +import android.view.MotionEvent; + +import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.UiThreadHelper; import com.android.quickstep.util.RemoteAnimationTargetSet; +import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; import java.util.ArrayList; import java.util.concurrent.ExecutorService; +import java.util.function.Supplier; /** * Wrapper around RecentsAnimationController to help with some synchronization @@ -43,6 +52,27 @@ public class RecentsAnimationWrapper { private final ExecutorService mExecutorService = new LooperExecutor(UiThreadHelper.getBackgroundLooper()); + private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + private InputConsumerController mInputConsumer = + InputConsumerController.getRecentsAnimationInputConsumer(); + private final Supplier mTouchProxySupplier; + + private boolean mInputConsumerUnregistered; + private boolean mTouchProxyEnabled; + + private TouchConsumer mTouchConsumer; + private boolean mTouchInProgress; + private boolean mInputConsumerUnregisterPending; + + private boolean mFinishPending; + + public RecentsAnimationWrapper(Supplier touchProxySupplier) { + // Register the input consumer on the UI thread, to ensure that it runs after any pending + // unregister calls + mTouchProxySupplier = touchProxySupplier; + mMainThreadExecutor.execute(mInputConsumer::registerInputConsumer); + } + public synchronized void setController( RecentsAnimationControllerCompat controller, RemoteAnimationTargetSet targetSet) { TraceHelper.partitionSection("RecentsController", "Set controller " + controller); @@ -77,21 +107,37 @@ public class RecentsAnimationWrapper { * on the background thread. */ public void finish(boolean toHome, Runnable onFinishComplete) { - mExecutorService.submit(() -> { - RecentsAnimationControllerCompat controller = mController; - mController = null; - TraceHelper.endSection("RecentsController", - "Finish " + controller + ", toHome=" + toHome); - if (controller != null) { - controller.setInputConsumerEnabled(false); - controller.finish(toHome); + if (!toHome) { + mExecutorService.submit(() -> finishBg(false, onFinishComplete)); + } + + mMainThreadExecutor.execute(() -> { + if (mTouchInProgress) { + mFinishPending = true; + // Execute the callback if (onFinishComplete != null) { onFinishComplete.run(); } + } else { + mExecutorService.submit(() -> finishBg(true, onFinishComplete)); } }); } + protected void finishBg(boolean toHome, Runnable onFinishComplete) { + RecentsAnimationControllerCompat controller = mController; + mController = null; + TraceHelper.endSection("RecentsController", "Finish " + controller + ", toHome=" + toHome); + if (controller != null) { + controller.setInputConsumerEnabled(false); + controller.finish(toHome); + + if (onFinishComplete != null) { + onFinishComplete.run(); + } + } + } + public void enableInputConsumer() { mInputConsumerEnabled = true; if (mInputConsumerEnabled) { @@ -106,6 +152,54 @@ public class RecentsAnimationWrapper { } } + public void unregisterInputConsumer() { + mMainThreadExecutor.execute(this::unregisterInputConsumerUi); + } + + private void unregisterInputConsumerUi() { + if (mTouchProxyEnabled && mTouchInProgress) { + mInputConsumerUnregisterPending = true; + } else { + mInputConsumerUnregistered = true; + mInputConsumer.unregisterInputConsumer(); + } + } + + public void enableTouchProxy() { + mMainThreadExecutor.execute(this::enableTouchProxyUi); + } + + private void enableTouchProxyUi() { + if (!mInputConsumerUnregistered) { + mTouchProxyEnabled = true; + mInputConsumer.setTouchListener(this::onInputConsumerTouch); + } + } + + private boolean onInputConsumerTouch(MotionEvent ev) { + int action = ev.getAction(); + if (action == ACTION_DOWN) { + mTouchInProgress = true; + mTouchConsumer = mTouchProxySupplier.get(); + } else if (action == ACTION_CANCEL || action == ACTION_UP) { + // Finish any pending actions + mTouchInProgress = false; + if (mInputConsumerUnregisterPending) { + mInputConsumerUnregisterPending = false; + mInputConsumer.unregisterInputConsumer(); + } + if (mFinishPending) { + mFinishPending = false; + mExecutorService.submit(() -> finishBg(true, null)); + } + } + if (mTouchConsumer != null) { + mTouchConsumer.accept(ev); + } + + return true; + } + public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { if (mBehindSystemBars == behindSystemBars) { return; diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java index 4cecffa2be..42e9aee1bf 100644 --- a/quickstep/src/com/android/quickstep/TouchConsumer.java +++ b/quickstep/src/com/android/quickstep/TouchConsumer.java @@ -29,6 +29,8 @@ import java.util.function.Consumer; @FunctionalInterface public interface TouchConsumer extends Consumer { + TouchConsumer NO_OP = (ev) -> {}; + @IntDef(flag = true, value = { INTERACTION_NORMAL, INTERACTION_QUICK_SCRUB diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index c555bc6f76..5a1f523391 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -21,7 +21,9 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; -import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; + +import static com.android.systemui.shared.system.ActivityManagerWrapper + .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; import android.annotation.TargetApi; @@ -33,7 +35,6 @@ import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; -import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; @@ -79,7 +80,7 @@ public class TouchInteractionService extends Service { private final IBinder mMyBinder = new IOverviewProxy.Stub() { @Override - public void onPreMotionEvent(@HitTarget int downHitTarget) throws RemoteException { + public void onPreMotionEvent(@HitTarget int downHitTarget) { TraceHelper.beginSection("SysUiBinder"); setupTouchConsumer(downHitTarget); TraceHelper.partitionSection("SysUiBinder", "Down target " + downHitTarget); @@ -155,8 +156,6 @@ public class TouchInteractionService extends Service { } }; - private final TouchConsumer mNoOpTouchConsumer = (ev) -> {}; - private static boolean sConnected = false; public static boolean isConnected() { @@ -185,7 +184,7 @@ public class TouchInteractionService extends Service { mMainThreadExecutor = new MainThreadExecutor(); mOverviewCommandHelper = new OverviewCommandHelper(this); mMainThreadChoreographer = Choreographer.getInstance(); - mEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer); + mEventQueue = new MotionEventQueue(mMainThreadChoreographer, TouchConsumer.NO_OP); mOverviewInteractionState = OverviewInteractionState.getInstance(this); mOverviewCallbacks = OverviewCallbacks.get(this); mTaskOverlayFactory = TaskOverlayFactory.get(this); @@ -229,10 +228,11 @@ public class TouchInteractionService extends Service { RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); if (runningTaskInfo == null && !forceToLauncher) { - return mNoOpTouchConsumer; + return TouchConsumer.NO_OP; } else if (forceToLauncher || runningTaskInfo.topActivity.equals(mOverviewCommandHelper.overviewComponent)) { - return getOverviewConsumer(); + return OverviewTouchConsumer.newInstance( + mOverviewCommandHelper.getActivityControlHelper(), false); } else { if (tracker == null) { tracker = VelocityTracker.obtain(); @@ -245,16 +245,16 @@ public class TouchInteractionService extends Service { } } - private TouchConsumer getOverviewConsumer() { - ActivityControlHelper activityHelper = mOverviewCommandHelper.getActivityControlHelper(); - BaseDraggingActivity activity = activityHelper.getCreatedActivity(); - if (activity == null) { - return mNoOpTouchConsumer; + private void initBackgroundChoreographer() { + if (sRemoteUiThread == null) { + sRemoteUiThread = new HandlerThread("remote-ui"); + sRemoteUiThread.start(); } - return new OverviewTouchConsumer(activityHelper, activity); + new Handler(sRemoteUiThread.getLooper()).post(() -> + mBackgroundThreadChoreographer = ChoreographerCompat.getSfInstance()); } - private static class OverviewTouchConsumer + public static class OverviewTouchConsumer implements TouchConsumer { private final ActivityControlHelper mActivityHelper; @@ -265,6 +265,8 @@ public class TouchInteractionService extends Service { private final int mTouchSlop; private final QuickScrubController mQuickScrubController; + private final boolean mStartingInActivityBounds; + private boolean mTrackingStarted = false; private boolean mInvalidated = false; @@ -272,11 +274,13 @@ public class TouchInteractionService extends Service { private boolean mStartPending = false; private boolean mEndPending = false; - OverviewTouchConsumer(ActivityControlHelper activityHelper, T activity) { + OverviewTouchConsumer(ActivityControlHelper activityHelper, T activity, + boolean startingInActivityBounds) { mActivityHelper = activityHelper; mActivity = activity; mTarget = activity.getDragLayer(); mTouchSlop = ViewConfiguration.get(mTarget.getContext()).getScaledTouchSlop(); + mStartingInActivityBounds = startingInActivityBounds; mQuickScrubController = mActivity.getOverviewPanel() .getQuickScrubController(); @@ -289,6 +293,10 @@ public class TouchInteractionService extends Service { } int action = ev.getActionMasked(); if (action == ACTION_DOWN) { + if (mStartingInActivityBounds) { + startTouchTracking(ev, false /* updateLocationOffset */); + return; + } mTrackingStarted = false; mDownPos.set(ev.getX(), ev.getY()); } else if (!mTrackingStarted) { @@ -301,13 +309,13 @@ public class TouchInteractionService extends Service { break; case ACTION_CANCEL: case ACTION_UP: - startTouchTracking(ev); + startTouchTracking(ev, true /* updateLocationOffset */); break; case ACTION_MOVE: { float displacement = ev.getY() - mDownPos.y; if (Math.abs(displacement) >= mTouchSlop) { // Start tracking only when mTouchSlop is crossed. - startTouchTracking(ev); + startTouchTracking(ev, true /* updateLocationOffset */); } } } @@ -322,8 +330,10 @@ public class TouchInteractionService extends Service { } } - private void startTouchTracking(MotionEvent ev) { - mTarget.getLocationOnScreen(mLocationOnScreen); + private void startTouchTracking(MotionEvent ev, boolean updateLocationOffset) { + if (updateLocationOffset) { + mTarget.getLocationOnScreen(mLocationOnScreen); + } // Send down touch event MotionEvent down = MotionEvent.obtain(ev); @@ -336,7 +346,7 @@ public class TouchInteractionService extends Service { private void sendEvent(MotionEvent ev) { int flags = ev.getEdgeFlags(); - ev.setEdgeFlags(flags | EDGE_NAV_BAR); + ev.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR); ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]); if (!mTrackingStarted) { mTarget.onInterceptTouchEvent(ev); @@ -411,14 +421,13 @@ public class TouchInteractionService extends Service { mQuickScrubController.onQuickScrubProgress(progress); } - } - - private void initBackgroundChoreographer() { - if (sRemoteUiThread == null) { - sRemoteUiThread = new HandlerThread("remote-ui"); - sRemoteUiThread.start(); + public static TouchConsumer newInstance( + ActivityControlHelper activityHelper, boolean startingInActivityBounds) { + BaseDraggingActivity activity = activityHelper.getCreatedActivity(); + if (activity == null) { + return TouchConsumer.NO_OP; + } + return new OverviewTouchConsumer(activityHelper, activity, startingInActivityBounds); } - new Handler(sRemoteUiThread.getLooper()).post(() -> - mBackgroundThreadChoreographer = ChoreographerCompat.getSfInstance()); } } diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index 0ce522adf9..d3d38af463 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -73,6 +73,7 @@ import com.android.quickstep.ActivityControlHelper.ActivityInitListener; import com.android.quickstep.ActivityControlHelper.AnimationFactory; import com.android.quickstep.ActivityControlHelper.LayoutListener; import com.android.quickstep.TouchConsumer.InteractionType; +import com.android.quickstep.TouchInteractionService.OverviewTouchConsumer; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.RemoteAnimationTargetSet; import com.android.quickstep.util.TransformedRect; @@ -80,7 +81,6 @@ import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.LatencyTrackerCompat; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -220,10 +220,8 @@ public class WindowTransformSwipeHandler { private @InteractionType int mInteractionType = INTERACTION_NORMAL; - private InputConsumerController mInputConsumer = - InputConsumerController.getRecentsAnimationInputConsumer(); - - private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper(); + private final RecentsAnimationWrapper mRecentsAnimationWrapper = + new RecentsAnimationWrapper(this::createNewTouchProxyHandler); private final long mTouchTimeMs; private long mLauncherFrameDrawnTime; @@ -247,9 +245,6 @@ public class WindowTransformSwipeHandler { .createActivityInitListener(this::onActivityInit); initStateCallbacks(); - // Register the input consumer on the UI thread, to ensure that it runs after any pending - // unregister calls - executeOnUiThread(mInputConsumer::registerInputConsumer); } private void initStateCallbacks() { @@ -695,6 +690,18 @@ public class WindowTransformSwipeHandler { } } + @UiThread + private TouchConsumer createNewTouchProxyHandler() { + mCurrentShift.finishAnimation(); + if (mLauncherTransitionController != null) { + mLauncherTransitionController.getAnimationPlayer().end(); + } + // Hide the task view, if not already hidden + setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha); + + return OverviewTouchConsumer.newInstance(mActivityControlHelper, true); + } + private void handleNormalGestureEnd(float endVelocity, boolean isFling) { float velocityPxPerMs = endVelocity / 1000; long duration = MAX_SWIPE_DURATION; @@ -737,6 +744,10 @@ public class WindowTransformSwipeHandler { } } } + if (goingToHome) { + mRecentsAnimationWrapper.enableTouchProxy(); + } + animateToProgress(startShift, endShift, duration, interpolator, goingToHome); } @@ -831,7 +842,7 @@ public class WindowTransformSwipeHandler { } mActivityInitListener.unregister(); - mInputConsumer.unregisterInputConsumer(); + mRecentsAnimationWrapper.unregisterInputConsumer(); mTaskSnapshot = null; } @@ -1059,7 +1070,7 @@ public class WindowTransformSwipeHandler { mLongSwipeController = mActivityControlHelper.getLongSwipeController( mActivity, mRunningTaskId); onLongSwipeDisplacementUpdated(); - setTargetAlphaProvider(mLongSwipeController::getTargetAlpha); + setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha); } private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) { @@ -1089,4 +1100,12 @@ public class WindowTransformSwipeHandler { private void preloadAssistData() { RecentsModel.getInstance(mContext).preloadAssistData(mRunningTaskId, mAssistData); } + + public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) { + if (!(app.isNotInRecents + || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) { + return 0; + } + return expectedAlpha; + } } diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 4f03bf00b8..3223837782 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -85,7 +85,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch // Usually we show the back button when a floating view is open. Instead, hide for these types. public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE; - public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE; + public static final int TYPE_ACCESSIBLE = TYPE_ALL + & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_QUICKSTEP_PREVIEW; protected boolean mIsOpen;