[CD] Handle trackpad back gestures on connected displays
This CL enables trackpad back gesture handling on connected displays in
EdgeBackGestureHandler.
Bug: 382774299
Test: Manual, i.e. verified that trackpad back gestures work on
connected displays.
Test: presubmit
Test: Automated tests in follow up CL
Flag: com.android.window.flags.enable_multidisplay_trackpad_back_gesture
Change-Id: I43d3a06c6c32fc2da1a3956bfe477a711e7aa17a
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java
index 180fedd..d0243cf 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java
@@ -49,7 +49,7 @@
void onMotionEvent(MotionEvent motionEvent);
/** Dumps info about the back gesture plugin. */
- void dump(PrintWriter pw);
+ void dump(String prefix, PrintWriter pw);
/** Callback to let the system react to the detected back gestures. */
interface BackCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index efed260..3f1401c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -239,6 +239,7 @@
@Override
public void onDisplayAddSystemDecorations(int displayId) {
CommandQueue.Callbacks.super.onDisplayAddSystemDecorations(displayId);
+ mEdgeBackGestureHandler.onDisplayAddSystemDecorations(displayId);
if (mLauncherProxyService.getProxy() == null) {
return;
}
@@ -253,6 +254,7 @@
@Override
public void onDisplayRemoved(int displayId) {
CommandQueue.Callbacks.super.onDisplayRemoved(displayId);
+ mEdgeBackGestureHandler.onDisplayRemoveSystemDecorations(displayId);
if (mLauncherProxyService.getProxy() == null) {
return;
}
@@ -267,6 +269,7 @@
@Override
public void onDisplayRemoveSystemDecorations(int displayId) {
CommandQueue.Callbacks.super.onDisplayRemoveSystemDecorations(displayId);
+ mEdgeBackGestureHandler.onDisplayRemoveSystemDecorations(displayId);
if (mLauncherProxyService.getProxy() == null) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 89f3a2c..b628a68 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -84,7 +84,7 @@
@AssistedInject
constructor(
@Assisted context: Context,
- private val windowManager: WindowManager,
+ @Assisted private val windowManager: WindowManager,
private val viewConfiguration: ViewConfiguration,
@Assisted private val mainHandler: Handler,
private val systemClock: SystemClock,
@@ -96,7 +96,11 @@
@AssistedFactory
interface Factory {
- fun create(context: Context, handler: Handler): BackPanelController
+ fun create(
+ context: Context,
+ windowManager: WindowManager,
+ handler: Handler,
+ ): BackPanelController
}
@VisibleForTesting internal var params: EdgePanelParams = EdgePanelParams(resources)
@@ -1018,10 +1022,10 @@
updateArrowState(GestureState.GONE, force = true)
}
- override fun dump(pw: PrintWriter) {
- pw.println("$TAG:")
- pw.println(" currentState=$currentState")
- pw.println(" isLeftPanel=${mView.isLeftPanel}")
+ override fun dump(prefix: String, pw: PrintWriter) {
+ pw.println("$prefix$TAG:")
+ pw.println("$prefix currentState=$currentState")
+ pw.println("$prefix isLeftPanel=${mView.isLeftPanel}")
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/DisplayBackGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/DisplayBackGestureHandler.kt
new file mode 100644
index 0000000..604d578b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/DisplayBackGestureHandler.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.gestural
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PixelFormat
+import android.graphics.Point
+import android.os.Trace
+import android.view.InputEvent
+import android.view.MotionEvent
+import android.view.WindowManager
+import com.android.systemui.plugins.NavigationEdgeBackPlugin
+import com.android.systemui.res.R
+import com.android.systemui.shared.system.InputChannelCompat
+import com.android.systemui.shared.system.InputMonitorCompat
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.BackPanelUiThread
+import com.android.systemui.util.concurrency.UiThreadContext
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.io.PrintWriter
+
+interface DisplayBackGestureHandler {
+
+ fun onMotionEvent(ev: MotionEvent)
+
+ fun setIsLeftPanel(isLeft: Boolean)
+
+ fun setBatchingEnabled(enabled: Boolean)
+
+ fun pilferPointers()
+
+ fun dispose()
+
+ fun dump(prefix: String, writer: PrintWriter)
+}
+
+class DisplayBackGestureHandlerImpl
+@AssistedInject
+constructor(
+ @Assisted private val context: Context,
+ @Assisted private val windowManager: WindowManager,
+ @Assisted private val onInputEvent: (InputEvent) -> Unit,
+ @Assisted backCallback: NavigationEdgeBackPlugin.BackCallback,
+ @BackPanelUiThread private val uiThreadContext: UiThreadContext,
+ private val backPanelControllerFactory: BackPanelController.Factory,
+ configurationControllerFactory: ConfigurationControllerImpl.Factory,
+) : DisplayBackGestureHandler {
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ context: Context,
+ windowManager: WindowManager,
+ backCallback: NavigationEdgeBackPlugin.BackCallback,
+ onInputEvent: (InputEvent) -> Unit,
+ ): DisplayBackGestureHandlerImpl
+ }
+
+ private val displayId = context.displayId
+ private val configurationController = configurationControllerFactory.create(context)
+ private val displaySize =
+ Point().apply {
+ val bounds = windowManager.maximumWindowMetrics.bounds
+ set(bounds.width(), bounds.height())
+ }
+ private val edgeBackPlugin = createEdgeBackPlugin(backCallback)
+
+ private val inputMonitorCompat = InputMonitorCompat("edge-swipe", displayId)
+ private val inputEventReceiver: InputChannelCompat.InputEventReceiver =
+ inputMonitorCompat.getInputReceiver(
+ uiThreadContext.looper,
+ uiThreadContext.choreographer,
+ ) { ev ->
+ onInputEvent(ev)
+ }
+
+ private val configurationListener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ newConfig?.windowConfiguration?.maxBounds?.let {
+ displaySize.set(it.width(), it.height())
+ edgeBackPlugin.setDisplaySize(displaySize)
+ }
+ }
+ }
+
+ init {
+ configurationController.addCallback(configurationListener)
+ }
+
+ override fun onMotionEvent(ev: MotionEvent) = edgeBackPlugin.onMotionEvent(ev)
+
+ override fun setIsLeftPanel(isLeft: Boolean) = edgeBackPlugin.setIsLeftPanel(isLeft)
+
+ override fun setBatchingEnabled(enabled: Boolean) =
+ inputEventReceiver.setBatchingEnabled(enabled)
+
+ override fun pilferPointers() = inputMonitorCompat.pilferPointers()
+
+ override fun dispose() {
+ inputEventReceiver.dispose()
+ inputMonitorCompat.dispose()
+ edgeBackPlugin.onDestroy()
+ configurationController.removeCallback(configurationListener)
+ }
+
+ private fun createEdgeBackPlugin(
+ backCallback: NavigationEdgeBackPlugin.BackCallback
+ ): BackPanelController {
+ val backPanelController =
+ backPanelControllerFactory.create(context, windowManager, uiThreadContext.handler)
+ backPanelController.init()
+
+ try {
+ Trace.beginSection("setEdgeBackPlugin")
+ backPanelController.setBackCallback(backCallback)
+ backPanelController.setLayoutParams(createLayoutParams())
+ backPanelController.setDisplaySize(displaySize)
+ } finally {
+ Trace.endSection()
+ }
+ return backPanelController
+ }
+
+ private fun createLayoutParams(): WindowManager.LayoutParams {
+ val resources = context.resources
+ val layoutParams =
+ WindowManager.LayoutParams(
+ resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
+ resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN),
+ PixelFormat.TRANSLUCENT,
+ )
+ layoutParams.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel)
+ layoutParams.windowAnimations = 0
+ layoutParams.privateFlags =
+ layoutParams.privateFlags or
+ (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS or
+ WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION)
+ layoutParams.title = "$TAG $displayId"
+ layoutParams.fitInsetsTypes = 0
+ layoutParams.setTrustedOverlay()
+ return layoutParams
+ }
+
+ override fun dump(prefix: String, pw: PrintWriter) {
+ pw.println("$prefix$TAG (displayId=$displayId)")
+ pw.println("$prefix displaySize=$displaySize")
+ pw.println("$prefix edgeBackPlugin=$edgeBackPlugin")
+ edgeBackPlugin.dump("$prefix ", pw)
+ }
+
+ companion object {
+ private const val TAG = "DisplayBackGestureHandler"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 51abb2d..e72f560 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static com.android.systemui.Flags.edgebackGestureHandlerGetRunningTasksBackground;
+import static com.android.window.flags.Flags.enableMultidisplayTrackpadBackGesture;
import static com.android.systemui.Flags.predictiveBackDelayWmTransition;
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
@@ -46,6 +47,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DisplayManager;
import android.hardware.input.InputManager;
import android.icu.text.SimpleDateFormat;
import android.os.Handler;
@@ -58,6 +60,7 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
+import android.view.Display;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
import android.view.InputDevice;
@@ -108,11 +111,14 @@
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import kotlin.Unit;
+
import kotlinx.coroutines.Job;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.Date;
+import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@@ -144,7 +150,7 @@
@Override
public void onSystemGestureExclusionChanged(int displayId,
Region systemGestureExclusion, Region unrestrictedOrNull) {
- if (displayId == mDisplayId) {
+ if (displayId == mMainDisplayId) {
mUiThreadContext.getExecutor().execute(() -> {
mExcludeRegion.set(systemGestureExclusion);
mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null
@@ -204,7 +210,7 @@
private final NavigationModeController mNavigationModeController;
private final BackPanelController.Factory mBackPanelControllerFactory;
private final ViewConfiguration mViewConfiguration;
- private final WindowManager mWindowManager;
+ private final WindowManager mDefaultWindowManager;
private final IWindowManager mWindowManagerService;
private final InputManager mInputManager;
private final Optional<Pip> mPipOptional;
@@ -213,7 +219,7 @@
private final Configuration mLastReportedConfig = new Configuration();
private final Point mDisplaySize = new Point();
- private final int mDisplayId;
+ private final int mMainDisplayId;
private final UiThreadContext mUiThreadContext;
private final Handler mBgHandler;
@@ -277,6 +283,8 @@
private InputMonitorCompat mInputMonitor;
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
+ private final Map<Integer, DisplayBackGestureHandler> mDisplayBackGestureHandlers =
+ new HashMap<>();
private NavigationEdgeBackPlugin mEdgeBackPlugin;
private BackAnimation mBackAnimation;
@@ -302,6 +310,11 @@
private LogArray mGestureLogOutsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
private SimpleDateFormat mLogDateFormat = new SimpleDateFormat("HH:mm:ss.SSS", Locale.US);
private Date mTmpLogDate = new Date();
+ private int mLastDownEventDisplayId;
+
+
+ private final DisplayManager mDisplayManager;
+ private final DisplayBackGestureHandlerImpl.Factory mDisplayBackGestureHandlerFactory;
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private final NotificationShadeWindowController mNotificationShadeWindowController;
@@ -458,9 +471,11 @@
Provider<LightBarController> lightBarControllerProvider,
NotificationShadeWindowController notificationShadeWindowController,
GestureInteractor gestureInteractor,
- JavaAdapter javaAdapter) {
+ JavaAdapter javaAdapter,
+ DisplayManager displayManager,
+ DisplayBackGestureHandlerImpl.Factory displayBackGestureHandlerFactory) {
mContext = context;
- mDisplayId = context.getDisplayId();
+ mMainDisplayId = context.getDisplayId();
mUiThreadContext = uiThreadContext;
mBackgroundExecutor = backgroundExecutor;
mBgHandler = bgHandler;
@@ -470,7 +485,7 @@
mNavigationModeController = navigationModeController;
mBackPanelControllerFactory = backPanelControllerFactory;
mViewConfiguration = viewConfiguration;
- mWindowManager = windowManager;
+ mDefaultWindowManager = windowManager;
mWindowManagerService = windowManagerService;
mInputManager = inputManager;
mPipOptional = pipOptional;
@@ -481,6 +496,8 @@
mGestureInteractor = gestureInteractor;
mJavaAdapter = javaAdapter;
mLastReportedConfig.setTo(mContext.getResources().getConfiguration());
+ mDisplayManager = displayManager;
+ mDisplayBackGestureHandlerFactory = displayBackGestureHandlerFactory;
ComponentName recentsComponentName = ComponentName.unflattenFromString(
context.getString(com.android.internal.R.string.config_recentsComponentName));
@@ -666,6 +683,58 @@
}
}
+ /**
+ * Called when a new display gets connected
+ *
+ * @param displayId The id associated with the connected display.
+ */
+ public void onDisplayAddSystemDecorations(int displayId) {
+ if (enableMultidisplayTrackpadBackGesture() && mIsEnabled) {
+ mUiThreadContext.runWithScissors(() -> {
+ removeAndDisposeDisplayResource(displayId);
+ mDisplayBackGestureHandlers.put(displayId,
+ createDisplayBackGestureHandler(displayId));
+ });
+ }
+ }
+
+ /**
+ * Called when a display gets disconnected
+ *
+ * @param displayId The id associated with the disconnected display.
+ */
+ public void onDisplayRemoveSystemDecorations(int displayId) {
+ if (enableMultidisplayTrackpadBackGesture()) {
+ mUiThreadContext.runWithScissors(() -> removeAndDisposeDisplayResource(displayId));
+ }
+ }
+
+ private DisplayBackGestureHandler createDisplayBackGestureHandler(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ Context windowContext = mContext.createWindowContext(display,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, null);
+ WindowManager displayWindowManager = mDefaultWindowManager;
+ if (displayId != mMainDisplayId) {
+ displayWindowManager = windowContext.getSystemService(WindowManager.class);
+ if (displayWindowManager == null) {
+ displayWindowManager = mDefaultWindowManager;
+ }
+ }
+ return mDisplayBackGestureHandlerFactory.create(windowContext, displayWindowManager,
+ mBackCallback, (ev) -> {
+ onInputEvent(ev);
+ return Unit.INSTANCE;
+ });
+ }
+
+ private void removeAndDisposeDisplayResource(int displayId) {
+ DisplayBackGestureHandler displayBackGestureHandler = mDisplayBackGestureHandlers.remove(
+ displayId);
+ if (displayBackGestureHandler != null) {
+ displayBackGestureHandler.dispose();
+ }
+ }
+
private void updateIsEnabled() {
mUiThreadContext.runWithScissors(this::updateIsEnabledInner);
}
@@ -681,11 +750,20 @@
return;
}
mIsEnabled = isEnabled;
- disposeInputChannel();
- if (mEdgeBackPlugin != null) {
- mEdgeBackPlugin.onDestroy();
- mEdgeBackPlugin = null;
+ if (enableMultidisplayTrackpadBackGesture()) {
+ for (DisplayBackGestureHandler displayBackGestureHandler :
+ mDisplayBackGestureHandlers.values()) {
+ displayBackGestureHandler.dispose();
+ }
+ mDisplayBackGestureHandlers.clear();
+ } else {
+ disposeInputChannel();
+
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.onDestroy();
+ mEdgeBackPlugin = null;
+ }
}
if (!mIsEnabled) {
@@ -701,7 +779,7 @@
try {
mWindowManagerService.unregisterSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
+ mGestureExclusionListener, mMainDisplayId);
} catch (RemoteException | IllegalArgumentException e) {
Log.e(TAG, "Failed to unregister window manager callbacks", e);
}
@@ -729,18 +807,28 @@
try {
mWindowManagerService.registerSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
+ mGestureExclusionListener, mMainDisplayId);
} catch (RemoteException | IllegalArgumentException e) {
Log.e(TAG, "Failed to register window manager callbacks", e);
}
- // Register input event receiver
- mInputMonitor = new InputMonitorCompat("edge-swipe", mDisplayId);
- mInputEventReceiver = mInputMonitor.getInputReceiver(mUiThreadContext.getLooper(),
- mUiThreadContext.getChoreographer(), this::onInputEvent);
+ if (enableMultidisplayTrackpadBackGesture()) {
+ // Registers input event receiver and adds a nav bar panel window
+ for (Display display : mDisplayManager.getDisplays()) {
+ int displayId = display.getDisplayId();
+ mDisplayBackGestureHandlers.put(displayId,
+ createDisplayBackGestureHandler(displayId));
+ }
+ } else {
+ // Register input event receiver
+ mInputMonitor = new InputMonitorCompat("edge-swipe", mMainDisplayId);
+ mInputEventReceiver = mInputMonitor.getInputReceiver(
+ mUiThreadContext.getLooper(),
+ mUiThreadContext.getChoreographer(), this::onInputEvent);
- // Add a nav bar panel window
- resetEdgeBackPlugin();
+ // Add a nav bar panel window
+ resetEdgeBackPlugin();
+ }
// Begin listening to changes in blocked activities list
mBlockedActivitiesJob = mJavaAdapter.alwaysCollectFlow(
@@ -758,7 +846,7 @@
private void resetEdgeBackPlugin() {
BackPanelController backPanelController = mBackPanelControllerFactory.create(mContext,
- mUiThreadContext.getHandler());
+ mDefaultWindowManager, mUiThreadContext.getHandler());
backPanelController.init();
setEdgeBackPlugin(backPanelController);
}
@@ -928,13 +1016,16 @@
return true;
}
- private boolean isValidTrackpadBackGesture(boolean isTrackpadEvent) {
- if (!isTrackpadEvent) {
- return false;
+
+ private boolean isValidTrackpadBackGesture(int displayId) {
+ if (enableMultidisplayTrackpadBackGesture() && displayId != mMainDisplayId) {
+ //TODO(b/382774299): Handle exclude regions on connected displays
+ return true;
}
// for trackpad gestures, unless the whole screen is excluded region, 3-finger swipe
// gestures are allowed even if the cursor is in the excluded region.
- WindowInsets windowInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+ WindowInsets windowInsets =
+ mDefaultWindowManager.getCurrentWindowMetrics().getWindowInsets();
Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
final Rect excludeBounds = mExcludeRegion.getBounds();
return !excludeBounds.contains(insets.left, insets.top, mDisplaySize.x - insets.right,
@@ -946,13 +1037,15 @@
}
private boolean isWithinTouchRegion(MotionEvent ev) {
- // If the point is inside the PiP or desktop excluded bounds, then ignore the back gesture
+ // If the point is inside the PiP or desktop excluded bounds, then ignore the back gesture.
+ // gesture. Also ignore (for now) if it's not on the main display.
+ // TODO(b/382130680): Implement back gesture handling on connected displays
int x = (int) ev.getX();
int y = (int) ev.getY();
final boolean isInsidePip = mIsInPip && mPipExcludedBounds.contains(x, y);
final boolean isInDesktopExcludeRegion = desktopExcludeRegionContains(x, y)
&& isEdgeResizePermitted(ev);
- if (isInsidePip || isInDesktopExcludeRegion) {
+ if (isInsidePip || isInDesktopExcludeRegion || ev.getDisplayId() != mMainDisplayId) {
return false;
}
@@ -1011,7 +1104,11 @@
mInRejectedExclusion = false;
MotionEvent cancelEv = MotionEvent.obtain(ev);
cancelEv.setAction(MotionEvent.ACTION_CANCEL);
- mEdgeBackPlugin.onMotionEvent(cancelEv);
+ if (enableMultidisplayTrackpadBackGesture()) {
+ mDisplayBackGestureHandlers.get(ev.getDisplayId()).onMotionEvent(cancelEv);
+ } else {
+ mEdgeBackPlugin.onMotionEvent(cancelEv);
+ }
dispatchToBackAnimation(cancelEv);
cancelEv.recycle();
}
@@ -1043,6 +1140,14 @@
private void onMotionEvent(MotionEvent ev) {
int action = ev.getActionMasked();
+ DisplayBackGestureHandler displayBackGestureHandler = mDisplayBackGestureHandlers.get(
+ ev.getDisplayId());
+ if (enableMultidisplayTrackpadBackGesture() && displayBackGestureHandler == null) {
+ Log.e(TAG, "Received MotionEvent on unknown display");
+ return;
+ }
+
+
if (action == MotionEvent.ACTION_DOWN) {
if (DEBUG_MISSING_GESTURE) {
Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev);
@@ -1052,7 +1157,11 @@
// Verify if this is in within the touch region and we aren't in immersive mode, and
// either the bouncer is showing or the notification panel is hidden
- mInputEventReceiver.setBatchingEnabled(false);
+ if (enableMultidisplayTrackpadBackGesture()) {
+ displayBackGestureHandler.setBatchingEnabled(false);
+ } else {
+ mInputEventReceiver.setBatchingEnabled(false);
+ }
if (mIsTrackpadThreeFingerSwipe) {
// Since trackpad gestures don't have zones, this will be determined later by the
// direction of the gesture. {@code mIsOnLeftEdge} is set to false to begin with.
@@ -1075,14 +1184,20 @@
boolean trackpadGesturesEnabled =
(mSysUiFlags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0;
mAllowGesture = isBackAllowedCommon && trackpadGesturesEnabled
- && isValidTrackpadBackGesture(true /* isTrackpadEvent */);
+ && isValidTrackpadBackGesture(ev.getDisplayId());
} else {
mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets
&& isWithinTouchRegion(ev) && !isButtonPressFromTrackpad(ev);
}
if (mAllowGesture) {
- mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
- mEdgeBackPlugin.onMotionEvent(ev);
+ if (enableMultidisplayTrackpadBackGesture()) {
+ displayBackGestureHandler.setIsLeftPanel(mIsOnLeftEdge);
+ displayBackGestureHandler.onMotionEvent(ev);
+ mLastDownEventDisplayId = ev.getDisplayId();
+ } else {
+ mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
+ mEdgeBackPlugin.onMotionEvent(ev);
+ }
dispatchToBackAnimation(ev);
}
if (mLogGesture || mIsTrackpadThreeFingerSwipe) {
@@ -1126,7 +1241,11 @@
// mIsOnLeftEdge is determined by the relative position between the down
// and the current motion event for trackpad gestures instead of zoning.
mIsOnLeftEdge = mEndPoint.x > mDownPoint.x;
- mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
+ if (enableMultidisplayTrackpadBackGesture()) {
+ displayBackGestureHandler.setIsLeftPanel(mIsOnLeftEdge);
+ } else {
+ mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
+ }
mDeferSetIsOnLeftEdge = false;
}
@@ -1163,7 +1282,7 @@
mBackAnimation.onThresholdCrossed();
}
if (mBackAnimation == null) {
- pilferPointers();
+ pilferPointers(ev.getDisplayId());
}
mThresholdCrossed = true;
} else {
@@ -1175,7 +1294,11 @@
if (mAllowGesture) {
// forward touch
- mEdgeBackPlugin.onMotionEvent(ev);
+ if (enableMultidisplayTrackpadBackGesture()) {
+ displayBackGestureHandler.onMotionEvent(ev);
+ } else {
+ mEdgeBackPlugin.onMotionEvent(ev);
+ }
dispatchToBackAnimation(ev);
if (predictiveBackDelayWmTransition() && mBackAnimation != null
&& mThresholdCrossed && !mLastFrameThresholdCrossed) {
@@ -1185,13 +1308,26 @@
}
}
- private void pilferPointers() {
- if (mInputMonitor != null) {
- // Capture inputs
- mInputMonitor.pilferPointers();
- // Notify FalsingManager that an intentional gesture has occurred.
- mFalsingManager.isFalseTouch(BACK_GESTURE);
- mInputEventReceiver.setBatchingEnabled(true);
+
+ private void pilferPointers(int displayId) {
+ if (enableMultidisplayTrackpadBackGesture()) {
+ DisplayBackGestureHandler displayBackGestureHandler = mDisplayBackGestureHandlers.get(
+ displayId);
+ if (displayBackGestureHandler != null) {
+ // Capture inputs
+ displayBackGestureHandler.pilferPointers();
+ // Notify FalsingManager that an intentional gesture has occurred.
+ mFalsingManager.isFalseTouch(BACK_GESTURE);
+ displayBackGestureHandler.setBatchingEnabled(true);
+ }
+ } else {
+ if (mInputMonitor != null) {
+ // Capture inputs
+ mInputMonitor.pilferPointers();
+ // Notify FalsingManager that an intentional gesture has occurred.
+ mFalsingManager.isFalseTouch(BACK_GESTURE);
+ mInputEventReceiver.setBatchingEnabled(true);
+ }
}
}
@@ -1239,7 +1375,7 @@
Log.d(DEBUG_MISSING_GESTURE_TAG, "Update display size: mDisplaySize=" + mDisplaySize);
}
- if (mEdgeBackPlugin != null) {
+ if (!enableMultidisplayTrackpadBackGesture() && mEdgeBackPlugin != null) {
mEdgeBackPlugin.setDisplaySize(mDisplaySize);
}
updateBackAnimationThresholds();
@@ -1251,6 +1387,8 @@
}
int maxDistance = mDisplaySize.x;
float linearDistance = Math.min(maxDistance, mBackSwipeLinearThreshold);
+ //TODO(b/382774299): Make sure we're updating mBackAnimation with the right thresholds for
+ // gestures on connected displays
mBackAnimation.setSwipeThresholds(linearDistance, maxDistance, mNonLinearFactor);
}
@@ -1312,9 +1450,17 @@
pw.println(" mTrackpadsConnected=" + mTrackpadsConnected.stream().map(
String::valueOf).collect(joining()));
pw.println(" mUsingThreeButtonNav=" + mUsingThreeButtonNav);
- pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin);
- if (mEdgeBackPlugin != null) {
- mEdgeBackPlugin.dump(pw);
+ if (enableMultidisplayTrackpadBackGesture()) {
+ pw.println(" mDisplayBackGestureHandlers:");
+ for (Map.Entry<Integer, DisplayBackGestureHandler> displayBackGestureHandlers :
+ mDisplayBackGestureHandlers.entrySet()) {
+ displayBackGestureHandlers.getValue().dump("\t", pw);
+ }
+ } else {
+ pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin);
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.dump("\t", pw);
+ }
}
}
@@ -1334,7 +1480,7 @@
if (backAnimation != null) {
final Executor uiThreadExecutor = mUiThreadContext.getExecutor();
backAnimation.setPilferPointerCallback(
- () -> uiThreadExecutor.execute(this::pilferPointers));
+ () -> uiThreadExecutor.execute(() -> pilferPointers(mLastDownEventDisplayId)));
backAnimation.setTopUiRequestCallback(
(requestTopUi, tag) -> uiThreadExecutor.execute(() ->
mNotificationShadeWindowController.setRequestTopUi(requestTopUi, tag)));