'Touch not detected on edges when touch is held

I have an app which detects swipe on edge of the screen using an overlay view. Now I wanted to add an action which will be performed when the overlayed view is touched and held without moving. However, I observed some weird behaviours, which I suspect, after hours of trying and searching, is caused by the accidental palm touch rejection feature of Android. My observations:

  • If I touch and hold for some duration and then release without moving, no event is detected. (this is the problem)
  • If I quickly touch and release, both action down and up events are generated consecutively.
  • The above behave the same way with both MotionEvent and onClickListener.
  • If I touch and hold as long as I want, and then move without releasing, both down and move events are generated as soon as move starts.
  • If I remove FLAG_NOT_FOCUSABLE and FLAG_NOT_TOUCH_MODAL flags and write 0 there, whole screen becomes touchable and detects the touches correctly, except the left/right edges.
  • If I place the overlay away from the edge towards middle of the screen, everything works correctly.
  • On android emulator, everything works well with mouse clicks on default overlay position.
  • So, I attached a mouse to my physical phone with OTG, it also works correctly with mouse clicks.
  • In the main settings activity of the app, same behaviour is observed on preference buttons. No button animation when pressing and holding on edge of the screen, but animation and click works if I quickly touch and release.

I tried all flags of WindowManager.LayoutParams, tried to capture dispatch events of the view, none worked.

Is the problem palm rejection feature? Is there a way to bypass this and detect touch and hold (preferably with custom hold duration) on edges of the screen on overlay?

Note: Tried on Pixel 2 XL, Android 11.

val overlayedButton = View(this)

val overlayedWidth = 30
val overlayedHeight = resources.displayMetrics.heightPixels / 2

val LAYOUT_FLAG: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
    @Suppress("DEPRECATION")
    WindowManager.LayoutParams.TYPE_PHONE
}

val params = WindowManager.LayoutParams(
    overlayedWidth,
    overlayedHeight,
    LAYOUT_FLAG,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
    PixelFormat.TRANSLUCENT
)

params.gravity = Gravity.RIGHT

params.x = 0
params.y = 0

if (overlayedButton!!.parent == null)
{
    wm!!.addView(overlayedButton, params)
}
else
{
    wm!!.updateViewLayout(overlayedButton, params)
}

overlayedButton!!.setOnTouchListener(this)
override fun onTouch(v: View, event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            //...
    }
        MotionEvent.ACTION_MOVE -> {
            //...
    }
        MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
            //...
    }
    //...
}
    


Solution 1:[1]

Maybe take a look at setSystemGestureExclusionRects. Also this article explains what is going on with the OS intercepting touches for high-level gestures.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ADB