Why is my system overlay catching all touches (no interaction with underlying view is possible)

Toni Kanoni picture Toni Kanoni · Mar 6, 2013 · Viewed 12.1k times · Source

I have a service which starts a system overlay. When the overlay is visible, I want it to be touchable/detect touches, but I want to preserve the interaction with the screen behind it (with behind I mean the underlying activity, next to the overlay for example).

My overlay is a bitmap/PNG of 128x128px. It gets drawn and when I click it, I receive the TOUCH! log, which is good. But also when I click on any other part of the screen (besides the overlay) I get the TOUCH! log and no interaction with the screen underneath it is possible.

Here are the parts of my code:

Main Activity to start the service

buttonStart.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // start Service and return to Homescreen
                Intent i = new Intent(MainActivity.this, MyService.class);
                startService(i);                

                Intent newActivity = new Intent(Intent.ACTION_MAIN); 
                newActivity.addCategory(Intent.CATEGORY_HOME);
                newActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(newActivity);
            }
        });

Service/Show overlay

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        myOverlay = new Overlay(MyService.this);

        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | 
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, 
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.LEFT | Gravity.TOP;

        wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.addView(myOverlay, params);

        myOverlay.setAnimation(new Waiting(MyService.this, 0, 0));
        myOverlay.postInvalidate();

        return super.onStartCommand(intent, flags, startId);
    }

Overlay

public MyOverlay(Context context) {
        super(context);     
        this.context = context;

        setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i("TOUCH!", event.getAction() + ", x:"+event.getX()+", y:"+event.getY());
                return false;
            }
        });
    }

What am I doing wrong? Also tried WindowManager.LayoutParams.TYPE_PHONE instead of WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, but same behaviour.

I am not using the Flag FLAG_WATCH_OUTSIDE_TOUCH, so why are the touches not registered next to the overlay?

EDIT

It seems I've found the problem, but still don't know how to solve it though... The displayed image in the overlay is really small and not the problem, but the overlay itself is full screen (!)

I added the following log to the Overlay / onTouch() event:

Log.i("TOUCH!", "View: "+v.toString() +", size="+ v.getWidth()+"/"+v.getHeight());

which then tells me View: package.overlay.myOverlay, size=480/762

Now I tried to force it not to be full screen by adding the following line in the Service:

    @Override
        public int onStartCommand(Intent intent, int flags, int startId) {

            myOverlay = new Overlay(MyService.this);
//HERE
myOVerlay.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

            WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | 
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, 
                    PixelFormat.TRANSLUCENT);
            params.gravity = Gravity.LEFT | Gravity.TOP;

            wm = (WindowManager) getSystemService(WINDOW_SERVICE);
            wm.addView(myOverlay, params);

            myOverlay.setAnimation(new Waiting(MyService.this, 0, 0));
            myOverlay.postInvalidate();

            return super.onStartCommand(intent, flags, startId);
        }

But it does not help... still myOverlay has the size of 480x762... Why is it that big and how can I get it to have the size of the contained image?

Answer

marmor picture marmor · Oct 27, 2014

This worked for me:

WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

Use this LayoutParams when adding the View to the WindowManager, it prevents it from receiving any clicks, it's possible that TYPE_TOAST is sufficient, but I've added flags just to make sure.