Allow quick scrub/switch from minus one
Launcher's window doesn't have focus when on minus one. In this case, we
tell the minus one overlay to hide and add a window focused callback to
start quick scrub/switch after launcher regains focus. Since the
transition from minus one takes longer than for launcher to get window
focus, we also defer until the overlay is completely hidden before
starting the quick scrub transition.
Bug: 70180755
Change-Id: Ifcf85aaf1942b51394e68e209b89807fa4007afe
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 0c5d961..d5fa723 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -21,7 +21,6 @@
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.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
@@ -41,13 +40,15 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherState;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.model.ModelPreload;
import com.android.launcher3.R;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.model.ModelPreload;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -216,9 +217,6 @@
}
View target = launcher.getDragLayer();
- if (!target.getWindowId().isFocused()) {
- return mNoOpTouchConsumer;
- }
return new LauncherTouchConsumer(launcher, target);
}
@@ -244,6 +242,9 @@
@Override
public void accept(MotionEvent ev) {
+ if (!mTarget.getWindowId().isFocused()) {
+ return;
+ }
int action = ev.getActionMasked();
if (action == ACTION_DOWN) {
mTrackingStarted = false;
@@ -294,6 +295,12 @@
public void updateTouchTracking(int interactionType) {
mMainThreadExecutor.execute(() -> {
if (TouchConsumer.isInteractionQuick(interactionType)) {
+ if (mLauncher.getWorkspace().runOnOverlayHidden(
+ () -> updateTouchTracking(interactionType))) {
+ // Hide the minus one overlay so launcher can get window focus.
+ mLauncher.onQuickstepGestureStarted(true);
+ return;
+ }
Runnable onComplete = null;
if (interactionType == INTERACTION_QUICK_SCRUB) {
mQuickScrubController.onQuickScrubStart(true);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2d24028..a083448 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -52,6 +52,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.widget.Toast;
import com.android.launcher3.Launcher.LauncherOverlay;
@@ -235,6 +236,7 @@
boolean mStartedSendingScrollEvents;
float mLastOverlayScroll = 0;
boolean mOverlayShown = false;
+ private Runnable mOnOverlayHiddenCallback;
private boolean mForceDrawAdjacentPages = false;
private boolean mPageRearrangeEnabled = false;
@@ -1163,6 +1165,10 @@
if (mOverlayShown) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+ if (mOnOverlayHiddenCallback != null) {
+ mOnOverlayHiddenCallback.run();
+ mOnOverlayHiddenCallback = null;
+ }
}
mOverlayShown = false;
}
@@ -1188,6 +1194,59 @@
mLauncher.getDragLayer().setAlpha(alpha);
}
+ /**
+ * Runs the given callback when the minus one overlay is hidden. Specifically, it is run
+ * when launcher's window has focus and the overlay is no longer being shown. If a callback
+ * is already present, the new callback will chain off it so both are run.
+ *
+ * @return Whether the callback was deferred.
+ */
+ public boolean runOnOverlayHidden(Runnable callback) {
+ View rootView = getRootView();
+ if (rootView.hasWindowFocus()) {
+ if (mOverlayShown) {
+ chainOverlayHiddenCallback(callback);
+ return true;
+ } else {
+ callback.run();
+ return false;
+ }
+ }
+ ViewTreeObserver observer = rootView.getViewTreeObserver();
+ if (observer != null && observer.isAlive()) {
+ observer.addOnWindowFocusChangeListener(
+ new ViewTreeObserver.OnWindowFocusChangeListener() {
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ if (hasFocus) {
+ // Defer further if the minus one overlay is still showing.
+ if (mOverlayShown) {
+ chainOverlayHiddenCallback(callback);
+ } else {
+ callback.run();
+ }
+ observer.removeOnWindowFocusChangeListener(this);
+ }
+ }
+ });
+ return true;
+ }
+ return false;
+ }
+
+ private void chainOverlayHiddenCallback(Runnable callback) {
+ if (mOnOverlayHiddenCallback == null) {
+ mOnOverlayHiddenCallback = callback;
+ } else {
+ // Chain the new callback onto the previous callback(s).
+ Runnable oldCallback = mOnOverlayHiddenCallback;
+ mOnOverlayHiddenCallback = () -> {
+ oldCallback.run();
+ callback.run();
+ };
+ }
+ }
+
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);