Android overlay to grab ALL touch, and pass them on?
Asked Answered
H

4

33

I'm basically trying to get all touch event data from something like a system overlay, move my sprites around based on this touch data, then allow the OS/homescreen/browser to act upon the data as it should(or vice versa). I have found similar questions, but nothing that leads me anywhere I haven't already been:

Getting the View that is receiving all the touch events

(Implemented, with results below) Creating a system overlay window (always on top)

What I can do:

I can EITHER grab ALL the touch events and act upon them by moving my sprites and not allow the OS/homescreen/browser to see any of them, OR ELSE I can allow the touch events to pass through and only get a “TOUCH_OUTSIDE” for my app to act upon.

My unattained goal:

I CAN NOT for the life of me figure out a way around getting BOTH to work with the data. The only methods I can think of, that I can't get implemented are: Intercepting the data in my APP and passing it onto OS/homescreen/browser to work with Allowing the OS/homescreen/browser to get the data first, and then getting a callback with information somehow Allowing the OS/homescreen/browser to get the data, act on the data, and the poll them for what their scroll/location values are so as to act upon it in my APP.

I fear that this just isn't possible, I think I read somewhere in some documentation that I can't find now: “It's all or nothing, either your view gets all the events, or none of them”

(To avoid confusion, I don't mean I have two views. I mean I have one view controlled via activity/service overlaying the OS/homescreen/browser. Like a pane of glass if you will.)

Thank you for any helpful information you can offer, it's very much appreciated!

[UPDATE] Posted my own documentation on the matter below, so as to not be confusing.

Hypolimnion answered 31/1, 2012 at 18:39 Comment(1)
is rooting an option, and are you trying to get this to work for only one device, or a generic device? I have a solution that involves rooting the phone and monitoring /dev/input/eventX, where X is the event stream that provides info about the touch event. This only works, however, if your device is rooted, and needs to be customized on a per-device basis.Blowy
H
18

Found this documentation that pretty much states that it's not possible to do both: Android : Multi touch and TYPE_SYSTEM_OVERLAY

They discuss workarounds but I don't think any of them will actually work for exactly what I'm trying to do. Both given the events to the underlying app, and being able to snoop them to act upon them for myself.

To create an overlay view, when setting up the LayoutParams you need to set the type to TYPE_SYSTEM_OVERLAY and use the flag FLAG_WATCH_OUTSIDE_TOUCH. This presents a problem because as the Android documentation states: "you will not receive the full down/move/up gesture, only the location of the first down as an ACTION_OUTSIDE." In order to receive the full array of touch events you need to use the TYPE_SYSTEM_ALERT type, but this causes the overlay to take over the screen and stop interaction with other elements.

Anyone wants to disagree I'd love to hear good news :-D

Hypolimnion answered 1/2, 2012 at 19:30 Comment(4)
Kindly view Pixel ruler from play store which recieves only events it wants ..play.google.com/store/apps/…Mutt
But i really dont have an idea how they did itMutt
I have worked on this problem quite a time now. As stated it is not possible to do both (capture all touch events and pass touch events to under layer) with Window Manager. However, I found the solution with listening getevents from logcat by creating a process. But this requires superuser.Passade
The Pixel Ruler is in the camp "Allow to pass events through", and I don't think it grabs touch events over transparent parts of the app; or at least I don't see any functionality that would require those events.Ramble
T
2

You can use the GestureOverlayView, if you want to hide the lines it draws you can set the color to Transparent #00000000 so it doesn't show up, and then you can capture all touches, and gestures.

http://developer.android.com/resources/articles/gestures.html

Twickenham answered 31/1, 2012 at 21:9 Comment(6)
not a solution. If I were building my own launcher, or home app, then yes. This however provides me with no more visibility/interception with regards to touch events..Hypolimnion
How do you figure? You can inherit from this control and intercept touch events and handle them Actiivity wide, despite your layout...Twickenham
I figure because I've tried it. In my question I say I'm overlaying infront of the OS/Browser/Launcher... everything. This simply provides me with the ability to be lazy and not code my own swipes and gestures? Still just a single touch event (OUTSIDE) fires when I scroll my home screen/ browser... Sorry if I confused you in the question.Hypolimnion
For clarification I want to both get the events AND scroll the screen, this is where I have issue. I can do either or, just not both as when you declare your overlay non_focusable so the backing gets scroll events, you only get one event. Then if you make it focusable you are stealing all events, and no way to give to underlying browser/app.Hypolimnion
you're still missing my point. I don't have a viewGroup, I don't have multiple views. Read the link I used below to fully understand what I'm talking about! It's a system_overlay which can't both intercept and pass on values....Hypolimnion
Yeah I obviously didn't fully understand how this aspect of Android worked, thus I asked the question? Isn't that what a community is for, when you hit a wall yourself ask others for their experience? Thanks for the pointless comment though! Glad you FINALLY understand what the actual question was though...Hypolimnion
Y
1

I was looking for the same thing.

This flag does the trick for me FLAG_NOT_TOUCH_MODAL

Works on Android 7 and 8 setup as below.

The only action I've implemented so far is a touch to close the overlay window.

I also used code from here Example System Overlay Code on Github which was needed to get the events.

By the way Google Maps does a really nice job with this on Android 8. You can drag their overlay window around, resize it or close it. And all other apps work fine while it's up.

    var type = 0

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
    {
        type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
    }
    else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    {
        type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
    }

    var flags = FLAG_NOT_TOUCH_MODAL

    mOverlayLayoutParams = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, type, flags, PixelFormat.TRANSLUCENT)
Yearround answered 12/2, 2018 at 0:2 Comment(1)
Doesn't do as OP requested. The link states "... even though other apps might be visible in the background they can't be interacted with."Ramble
Q
0

This is not the optimal solution but it works. Restart the service toggling the flag not touchable. put boolean extra to Intent which is used to startservice to determine previous state toggle value. A better implementation would be to fire the intent on touch when the window is listening and after a fixed period when not listening.

public class bleh extends Service {
    public void onCteqwer(int i) {

        Context context; Class <bleh> context1 = bleh.class;

        WindowManager.LayoutParams params = null;
        WindowManager mang = (WindowManager) getSystemService(WINDOW_SERVICE);

        //check previous state of service
        if(i==0)
            params = new WindowManager.LayoutParams(arg0,arg1,arg2,arg3,arg4,arg5);

        if(i==1)
            params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,arg0,arg1,arg2,arg3,arg4);

        LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
        View mViw = inflater.inflate(arg, null);

        mViw.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return true;
            }
        });

        mang.addView(mViw, params);

        Intent z = new Intent(context, context1);

        if(i==0)
            z.putExtra("name", 1);
        if(i==1)
            z.putExtra("name", 0);

        stopSelf();
        startService(z);
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        Bundle i=intent.getExtras();
        int userName = 0;

        if (i != null)
        {
            userName = i.getInt("name");
            onCteqwer(userName);
        }
    }
}
Quickwitted answered 11/7, 2014 at 23:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.