15

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?

Toni Kanoni
  • 2,265
  • 4
  • 23
  • 29
  • 1
    How big is your overlay? Does it cover the whole underlying layout? Is it a transparent thing? – abbath Mar 06 '13 at 15:18
  • 1
    "but I want to preserve the interaction with the screen behind it" -- fortunately, this is no longer possible as of Android 4.0.3, to prevent tapjacking attacks, at least for touches on the overlay itself. An overlay can either receive touch events (and those are not forwarded along) or not receive touch events (akin to a `Toast`). – CommonsWare Mar 06 '13 at 15:43
  • @abbath Its a 128x128 px bitmap drawn to a canvas. It has a transparent background, yes, which means i should probably make it opaque to test if its really that small... i will try that out – Toni Kanoni Mar 06 '13 at 21:10
  • @CommonsWare Maybe interaction was the false word. I dont want to interact, I just want to fetch clicks on the overlay, while for example pressing the messages icon on the homescreen still opens the messages app. Im pretty sure I have seen this done by another app on my device (ICS) – Toni Kanoni Mar 06 '13 at 21:14
  • "I just want to fetch clicks on the overlay, while for example pressing the messages icon on the homescreen still opens the messages app" -- if the "messages icon" is behind the overlay, this should no longer work, as of Android 4.0.3. – CommonsWare Mar 06 '13 at 21:15
  • With behind, you mean directly behind (=overlapping), right? If so, then this is ok, but i have a small overlay at the top of the screen and still cant start anything, even at the bottom of the homescreen. So maybe my overlay is bigger than im thinking and i didnt see it because of the transparency?? I will report back – Toni Kanoni Mar 06 '13 at 21:54
  • I have found the error but don't know how to solve it: The overlay is full screen (see my initial question, I have edited it). Can you help me on that? – Toni Kanoni Mar 07 '13 at 12:05
  • Have you fixed this now? – Samuel Agbede Jul 06 '17 at 07:14
  • any news regarding this? have the same issue on some devices – smedasn Dec 20 '21 at 16:42

3 Answers3

14

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.

marmor
  • 27,641
  • 11
  • 107
  • 150
2

I also had the same problem and I fix the problem by set the params.width & params.height. Try this.

params.width = 128;
params.height = 128;

I hope it'll help.

smartsys
  • 21
  • 4
-2

I had the same problem, just try to add this to your code:

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY

I'm pretty confident the code will work, if it doesn't just check the code for some other errors but as far as the touch is concerned, this is your solution. In any case just let me know. and i almost forgot the second part, just change the attributes to matchparent instead of wrapcontent.

Good Luck

Mayank
  • 1,364
  • 1
  • 15
  • 29
  • I think this is the opposite of what the OP wanted, this makes your view catch all clicks. – marmor Oct 27 '14 at 06:48
  • 1
    @marmor, in recent versions of Android, `TYPE_SYSTEM_OVERLAY` windows can't receive touch events. – Sam Dec 23 '14 at 21:09