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
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 0eead88..1d1b7da 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 @@
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 cfd4119..9a920c8 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 @@
// 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 2fe7a11..16214dd 100644
--- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java
+++ b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
@@ -169,12 +169,4 @@
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 b0313fc..eea3971 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 @@
private final ExecutorService mExecutorService =
new LooperExecutor(UiThreadHelper.getBackgroundLooper());
+ private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
+ private InputConsumerController mInputConsumer =
+ InputConsumerController.getRecentsAnimationInputConsumer();
+ private final Supplier<TouchConsumer> mTouchProxySupplier;
+
+ private boolean mInputConsumerUnregistered;
+ private boolean mTouchProxyEnabled;
+
+ private TouchConsumer mTouchConsumer;
+ private boolean mTouchInProgress;
+ private boolean mInputConsumerUnregisterPending;
+
+ private boolean mFinishPending;
+
+ public RecentsAnimationWrapper(Supplier<TouchConsumer> 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 @@
* 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 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 4cecffa..42e9aee 100644
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -29,6 +29,8 @@
@FunctionalInterface
public interface TouchConsumer extends Consumer<MotionEvent> {
+ 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 c555bc6..5a1f523 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_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.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 @@
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 @@
}
};
- private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
-
private static boolean sConnected = false;
public static boolean isConnected() {
@@ -185,7 +184,7 @@
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 @@
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 @@
}
}
- 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<T extends BaseDraggingActivity>
+ public static class OverviewTouchConsumer<T extends BaseDraggingActivity>
implements TouchConsumer {
private final ActivityControlHelper<T> mActivityHelper;
@@ -265,6 +265,8 @@
private final int mTouchSlop;
private final QuickScrubController mQuickScrubController;
+ private final boolean mStartingInActivityBounds;
+
private boolean mTrackingStarted = false;
private boolean mInvalidated = false;
@@ -272,11 +274,13 @@
private boolean mStartPending = false;
private boolean mEndPending = false;
- OverviewTouchConsumer(ActivityControlHelper<T> activityHelper, T activity) {
+ OverviewTouchConsumer(ActivityControlHelper<T> activityHelper, T activity,
+ boolean startingInActivityBounds) {
mActivityHelper = activityHelper;
mActivity = activity;
mTarget = activity.getDragLayer();
mTouchSlop = ViewConfiguration.get(mTarget.getContext()).getScaledTouchSlop();
+ mStartingInActivityBounds = startingInActivityBounds;
mQuickScrubController = mActivity.<RecentsView>getOverviewPanel()
.getQuickScrubController();
@@ -289,6 +293,10 @@
}
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 @@
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 @@
}
}
- 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 @@
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 @@
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 0ce522a..d3d38af 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.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.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 @@
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 @@
.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 @@
}
}
+ @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 @@
}
}
}
+ if (goingToHome) {
+ mRecentsAnimationWrapper.enableTouchProxy();
+ }
+
animateToProgress(startShift, endShift, duration, interpolator, goingToHome);
}
@@ -831,7 +842,7 @@
}
mActivityInitListener.unregister();
- mInputConsumer.unregisterInputConsumer();
+ mRecentsAnimationWrapper.unregisterInputConsumer();
mTaskSnapshot = null;
}
@@ -1059,7 +1070,7 @@
mLongSwipeController = mActivityControlHelper.getLongSwipeController(
mActivity, mRunningTaskId);
onLongSwipeDisplacementUpdated();
- setTargetAlphaProvider(mLongSwipeController::getTargetAlpha);
+ setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
}
private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) {
@@ -1089,4 +1100,12 @@
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 4f03bf0..3223837 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -85,7 +85,8 @@
// 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;