WindowManager not allowing to pass touch
Asked Answered
A

1

11

I have drawn an overlay using WindowManager. it's width and height are WindowManager.LayoutParams.MATCH_PARENT with transparent background.

My view must be match parent and handle circle touch listener and pass the rest touch to the below screen

I have two small circle in left and right corner. I make them draggable on screen that's working fine. But when I click on visible Home Screen button. WindowManager don't let the visible item clickable.

        int LAYOUT_FLAG = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.
                LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE;

        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.MATCH_PARENT,//changed it to full
                WindowManager.LayoutParams.MATCH_PARENT,
                LAYOUT_FLAG,

                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                |WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                ///| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, this flag can make it in touchable.
                ///WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                PixelFormat.TRANSLUCENT);

        mFloatingView = LayoutInflater.from(this).inflate(R.layout.item_circle_dragging, null);

        mFloatingView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                return false;
            }
        });
        //Add the view to the window
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mWindowManager.addView(mFloatingView,params);

item_circle_dragging.xml

<?xml version="1.0" encoding="utf-8"?>
<customviewpracticing.CircleDraggingView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</customviewpracticing.CircleDraggingView>

CircleDragginView onTouchEvent

@Override
public boolean onTouchEvent(MotionEvent event) {

   switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
          if(isPointInside(event.getRawX(),event.getRawY()))
            isAllowedToDrag = true;
          Log.d(TAG, "ACTION_DOWN:getRawX= " + event.getRawX() + " getRawY= " + event.getRawY() + " getX= "
                + event.getX() + " getY= " + event.getY());
          break;
        ///return true;
      case MotionEvent.ACTION_MOVE:
          Log.d(TAG, "ACTION_MOVE:getRawX= " + event.getRawX() + " getRawY= " + event.getRawY() + " getX= "
                + event.getX() + " getY= " + event.getY());
          if(isAllowedToDrag){
            center_circle_X = event.getRawX() ;
            center_circle_Y = event.getRawY();
            }/*this.animate().x(event.getRawX()).y(event.getRawY())
                    .setDuration(50).start();*/
          break;
      case MotionEvent.ACTION_UP:
         if(isAllowedToDrag)
            isAllowedToDrag = false;
         break;
      default:
        return true;//I changed it
   }
    // Force a view to draw again
    ///postInvalidate();
    invalidate();
    return true;
}

Also tried returning false in onTouchEvent of CircleDraggingView and also to main mFloatingView (Root View ).

Aegaeon answered 20/8, 2018 at 6:5 Comment(4)
Any luck with this?Incompletion
Any Luck on this ?Hydrogenate
any answer? I have also same problemJumbuck
You have to add two different views to the window manager, one of them (which) covers rhe whole screen and another one which is the touchable circle (if there are 2 circles the you need 3 views) and after any change to your circle you should also update the other view (which covers the whole screen)Undergrown
C
1

You can't do that on the same view I'm afraid, because the touch event either enters to your view, or it does not. Whether you're using intercept, returning false etc... They all depend on whether the event actually reaches your view, and even then, returning false wouldn't pass the event to the below screen, because they are different processes. In your case, the event wouldn't reach your window, and that would mean you can't handle the event at all.

I'm not entirely sure what the parent does, I believe you're trying to move the circle view here after touching it, with a drag operation or some sort. So, I'll give an example approach on how you can do it.

Adding the view in left and right corner can be specified in the layout params you are creating. For instance, top-left would look like this:

params.x = 0;
paramx.y = 0;

Top-right would look like this:

WindowManager manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

Point size = new Point();

// Get the screen dimensions here
manager.getDefaultDisplay().getSize(point);
int screenWidth = point.x;

// Assuming your view has a 40 dp width and height,
// calculate its pixel variant
int circleWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DP, 40, getResources().getDisplayMetrics());

// Target the top-right corner of the screen.
params.x = screenWidth - circleWidth;

// Y coordinate would be 0.
params.y = 0;

And view parameters would look like this:

// Assuming your view has a 40 dp width and height,
// calculate its pixel variant
int circleSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DP, 40, getResources().getDisplayMetrics());

WindowManager.LayoutParams params = new WindowManager.LayoutParams(circleSize /* width */, 
circleSize /* height */,
 0 /* x */,
 0 /* y */,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY /* windowType */,
 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH /* outsideTouch */,
 PixelFormat.TRANSLUCENT /* format */);

After that, moving the view would actually require to change your layout params and updating the view layout. In your onTouchEvent you can do something like this:

// Assuming you know the new x and y values of the view, you can
// update the layout params of the view like this:
((WindowManager.LayoutParams) getLayoutParams()).x = targetX;
((WindowManager.LayoutParams) getLayoutParams()).y = targetY;

// To update the view, you need to either call requestLayout() or
// do it from window manager directly.

// If the first way doesn't work, 2nd will work.

// First way:
requestLayout();

// Second way:

// This will get the same window manager the service has.
WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);

// Update the view layout afterwards.
manager.updateViewLayout(this, getLayoutParams());

That way, you can move the view with the touch event.

Couple things to note:

  1. If you can capture the onTouchEvent you know that you've touched the circle, so you don't need to perform coordinate checks by using the raw values (I'm referring to isPointInside() function). Returning true from all of them will help you receive all the events, but that's not necessary. You probably know how the lifecycle works anyway.
  2. Each circle must be added to the window manager as root with WindowManager.addView() so that the touches do not interfere each other, and touching a point where circle is not in will pass the event to the below screen.

This would be a start for your requirement, and I think you can fit this with your needs.

Conan answered 19/4, 2021 at 7:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.