Android registration screen touch event interception event distribution
- Preface
- Native sliding return gesture interception distribution analysis
- end
Foreword
After receiving the demand, it is necessary to realize the corresponding function to be triggered by sliding up and down with two fingers on the screen, and to intercept sliding conflicts;
Currently, many types of functions can be seen in the world, such as side swiping to return, three-finger swiping to take screenshots, etc.;
This time it is based on Android10, and the application is a system application!
No source code for three-finger sliding has been found in the native code, but fortunately there is a side-swipe return to the source code. When used on the machine, it can be seen that sliding conflicts are handled.
This article is based on Android10 source code
Native sliding return gesture interception distribution analysis
Source code link: http://aospxref.com/android-10.0.0_r47/xref/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
Mainly look at the updateIsEnabled method
private void updateIsEnabled() {<!-- --> boolean isEnabled = mIsAttached & amp; & amp; mIsGesturalModeEnabled; if (isEnabled == mIsEnabled) {<!-- --> return; } mIsEnabled = isEnabled; disposeInputChannel(); if (mEdgePanel != null) {<!-- --> mWm.removeView(mEdgePanel); mEdgePanel = null; mRegionSamplingHelper.stop(); mRegionSamplingHelper = null; } if (!mIsEnabled) {<!-- --> WindowManagerWrapper.getInstance().removePinnedStackListener(mImeChangedListener); mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); try {<!-- --> WindowManagerGlobal.getWindowManagerService() .unregisterSystemGestureExclusionListener( mGestureExclusionListener, mDisplayId); } catch (RemoteException e) {<!-- --> Log.e(TAG, "Failed to unregister window manager callbacks", e); } } else {<!-- --> updateDisplaySize(); //#1 Register screen events mContext.getSystemService(DisplayManager.class).registerDisplayListener(this, mContext.getMainThreadHandler()); try {<!-- --> WindowManagerWrapper.getInstance().addPinnedStackListener(mImeChangedListener); WindowManagerGlobal.getWindowManagerService() .registerSystemGestureExclusionListener( mGestureExclusionListener, mDisplayId); } catch (RemoteException e) {<!-- --> Log.e(TAG, "Failed to register window manager callbacks", e); } //#2 Register screen touch event // Register input event receiver mInputMonitor = InputManager.getInstance().monitorGestureInput( "edge-swipe", mDisplayId); mInputEventReceiver = new SysUiInputEventReceiver( mInputMonitor.getInputChannel(), Looper.getMainLooper()); // Add a nav bar panel window mEdgePanel = new NavigationBarEdgePanel(mContext); mEdgePanelLp = new WindowManager.LayoutParams( mContext.getResources() .getDimensionPixelSize(R.dimen.navigation_edge_panel_width), mContext.getResources() .getDimensionPixelSize(R.dimen.navigation_edge_panel_height), WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT); mEdgePanelLp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; mEdgePanelLp.setTitle(TAG + mDisplayId); mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel); mEdgePanelLp.windowAnimations = 0; mEdgePanel.setLayoutParams(mEdgePanelLp); mWm.addView(mEdgePanel, mEdgePanelLp); mRegionSamplingHelper = new RegionSamplingHelper(mEdgePanel, new RegionSamplingHelper.SamplingCallback() {<!-- --> @Override public void onRegionDarknessChanged(boolean isRegionDark) {<!-- --> mEdgePanel.setIsDark(!isRegionDark, true /* animate */); } @Override public Rect getSampledRegion(View sampledView) {<!-- --> return mSamplingRect; } }); } } //...omit part of the code private void onInputEvent(InputEvent ev) {<!-- --> if (ev instanceof MotionEvent) {<!-- --> onMotionEvent((MotionEvent) ev); } } private boolean isWithinTouchRegion(int x, int y) {<!-- --> if (y > (mDisplaySize.y - Math.max(mImeHeight, mNavBarHeight))) {<!-- --> return false; } if (x > mEdgeWidth + mLeftInset & amp; & amp; x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {<!-- --> return false; } boolean isInExcludedRegion = mExcludeRegion.contains(x, y); if (isInExcludedRegion) {<!-- --> mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1, false /* isButton */, !mIsOnLeftEdge); } return !isInExcludedRegion; } private void cancelGesture(MotionEvent ev) {<!-- --> // Send action cancel to reset all the touch events mAllowGesture = false; MotionEvent cancelEv = MotionEvent.obtain(ev); cancelEv.setAction(MotionEvent.ACTION_CANCEL); mEdgePanel.handleTouch(cancelEv); cancelEv.recycle(); } private void onMotionEvent(MotionEvent ev) {<!-- --> int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) {<!-- --> // 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 int stateFlags = mOverviewProxyService.getSystemUiStateFlags(); mIsOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset; mAllowGesture = !QuickStepContract.isBackGestureDisabled(stateFlags) & amp; & amp; isWithinTouchRegion((int) ev.getX(), (int) ev.getY()); if (mAllowGesture) {<!-- --> mEdgePanelLp.gravity = mIsOnLeftEdge ? (Gravity.LEFT | Gravity.TOP) : (Gravity.RIGHT | Gravity.TOP); mEdgePanel.setIsLeftPanel(mIsOnLeftEdge); mEdgePanel.handleTouch(ev); updateEdgePanelPosition(ev.getY()); mWm.updateViewLayout(mEdgePanel, mEdgePanelLp); mRegionSamplingHelper.start(mSamplingRect); mDownPoint.set(ev.getX(), ev.getY()); mThresholdCrossed = false; } } else if (mAllowGesture) {<!-- --> if (!mThresholdCrossed) {<!-- --> if (action == MotionEvent.ACTION_POINTER_DOWN) {<!-- --> // We do not support multi touch for back gesture // Cancel the gesture after multi-finger touch. Experiment with the phone and you can see that the event will be canceled when we add another finger when sliding with one finger. cancelGesture(ev); return; } else if (action == MotionEvent.ACTION_MOVE) {<!-- --> if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {<!-- --> cancelGesture(ev); return; } float dx = Math.abs(ev.getX() - mDownPoint.x); float dy = Math.abs(ev.getY() - mDownPoint.y); if (dy > dx & amp; & amp; dy > mTouchSlop) {<!-- --> //If you slide vertically, cancel the event cancelGesture(ev); return; } else if (dx > dy & amp; & amp; dx > mTouchSlop) {<!-- --> mThresholdCrossed = true; // Capture inputs // Capture the current gesture to prevent interference with the interface mInputMonitor.pilferPointers(); } } } // forward touch mEdgePanel.handleTouch(ev); boolean isUp = action == MotionEvent.ACTION_UP; if (isUp) {<!-- --> boolean performAction = mEdgePanel.shouldTriggerBack(); if (performAction) {<!-- --> // Perform back sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK); } mOverviewProxyService.notifyBackAction(performAction, (int) mDownPoint.x, (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge); } if (isUp || action == MotionEvent.ACTION_CANCEL) {<!-- --> mRegionSamplingHelper.stop(); } else {<!-- --> updateSamplingRect(); mRegionSamplingHelper.updateSamplingRect(); } } } \t class SysUiInputEventReceiver extends InputEventReceiver {<!-- --> SysUiInputEventReceiver(InputChannel channel, Looper looper) {<!-- --> super(channel, looper); } public void onInputEvent(InputEvent event) {<!-- --> EdgeBackGestureHandler.this.onInputEvent(event); finishInputEvent(event, true); } }
1. Register screen event code
///#1 Register screen events mContext.getSystemService(DisplayManager.class).registerDisplayListener(this, mContext.getMainThreadHandler());
///#2 Register screen touch events // Register input event receiver mInputMonitor = InputManager.getInstance().monitorGestureInput( "edge-swipe", mDisplayId); mInputEventReceiver = new SysUiInputEventReceiver( mInputMonitor.getInputChannel(), Looper.getMainLooper());
2. Check the touch logic
The final trend of the event is onMotionEvent
You can see that it will be intercepted after it meets the side sliding gesture.
// Capture the current gesture to prevent interference with the interface mInputMonitor.pilferPointers();
It’s almost done here. We only need to cancel and intercept according to the corresponding gesture function to realize the sliding conflict problem;
End
Thanks to Android for providing the source code
Thanks to Qiyuexiaxun for analyzing the gesture code and Android gesture navigation core implementation.
This article only provides a general idea about sliding conflicts. The article may involve errors or deficiencies. Thank you for your correction!