How can a service listen for touch gestures/events?

AndroidEventsServiceTouchGesture

Android Problem Overview


I'm wondering how apps like SwipePad and Wave Launcher are able to detect touch gestures/events simply through a service. These apps are able to detect a touch gestures even though it is not in their own Activity. I've looked all over the Internet and haven't found how they can do that.

My main question is how a service can listen in on touch guestures/events just as a regular Activity may receive MotionEvents even though it may not be in the original Activity or context. I'm essentially trying a build an app that will recongize a particular touch gesture from a user regardless which Activity is on top and do something when that gesture is recongized. The touch recongition will be a thread running in the background as a service.

Android Solutions


Solution 1 - Android

I had this same problem and I've finally figured it out! Thanks to this post: https://stackoverflow.com/questions/4481226/creating-a-system-overlay-always-on-top-button-in-android. You need to use an alert window instead of an overlay (and this also means you can use it in Andoid ICS):

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);

Then just attach a GestureListener in this manner:

GestureDetector gestureDetector = new GestureDetector(this, new AwesomeGestureListener());
View.OnTouchListener gestureListener = new View.OnTouchListener() {
      public boolean onTouch(View v, MotionEvent event) {
            return gestureDetector.onTouchEvent(event);
      }
};
        
overlayView.setOnTouchListener(gestureListener);

Yay!

Solution 2 - Android

Interesting question. I don't know how they did that and I found google group posts which tell me that there is no global touch listener. But I have an idea anyways...

I found this post where someone succeeds to display a popupwindow from a service. If I would make that popup transparent and fullscreen, I'm sure I could capture the touches since I'm allowed to set a touch interceptor.

Edit: Please report results when you try that, would be interesting to know if this works...

Solution 3 - Android

I tested every possible solution but nothing worked some didn't fired touch event which did they frozed the screen .

So I did some reverse engineering and now posting solution which works

  WindowManager.LayoutParams params = new android.view.WindowManager.LayoutParams(0, 0, 0, 0, 2003, 0x40028, -3);
        View mView = new View(this);

        mView.setOnTouchListener(this);

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

Solution 4 - Android

I tested this on API 21 and a nexus 5 and I was able to log, from a service, when a touch event was fired by the system. However, the data inside the MotionEvent object, for example coordinates, returns 0.0 when OUTSIDE of what seems to be my app's package.

Two sources integration, permission

I'm curious as to why the event.getX(), event.getY() and event.getPressure() return 0.0 when not in an activity of the app where the service lives.

Edit

This solution does not prevent other listeners from receiving the touch event capured by the service because of

public boolean onTouch(View v, MotionEvent e){
    Log.d(LOG_TAG, "Touch event captured");
    return false;      

By returning false, it allows other listeners to receive the event.

Edit2

Apparently, the input dispatcher in the OS will set the coordinates and pressure to 0 if the current activity and the listener do not share an UID, here is source

Solution 5 - Android

I've searched through many SO threads, but for security reasons, I don't think this is possible for all packages on the system. It certainly is for your own app though.

WindowManager

 WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT);

Floating / Overlay layout

floatyView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate
            (R.layout.floating_view, null);

    floatyView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.d(TAG, "event.x " + event.getX());
            Log.d(TAG, "event.y " + event.getY());
            if (event.getAction() == MotionEvent.ACTION_UP) {
                v.performClick();
            }
            return false;
        }
    });

Layout

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:background="@android:color/transparent"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBrianView Question on Stackoverflow
Solution 1 - AndroidpypmannetjiesView Answer on Stackoverflow
Solution 2 - AndroidKnickediView Answer on Stackoverflow
Solution 3 - AndroidMr CoderView Answer on Stackoverflow
Solution 4 - AndroidClockerView Answer on Stackoverflow
Solution 5 - AndroidJohn61590View Answer on Stackoverflow