19

How can I reliably simulate touch events on Android (without rooting) from Java outside my app which runs as a background service?

While this question has been asked before, most answers utilise ADB. (such as How to simulate touch events on Android device?)

https://github.com/chetbox/android-mouse-cursor offers a good solution using Accessibility, but is not very reliable as not all views respond to it, and games do not respond at all most of the time.

private void click() {
  AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
  if (nodeInfo == null) return;

  AccessibilityNodeInfo nearestNodeToMouse = findSmallestNodeAtPoint(nodeInfo, cursorLayout.x, cursorLayout.y + 50);

  if (nearestNodeToMouse != null) {
    logNodeHierachy(nearestNodeToMouse, 0);
    nearestNodeToMouse.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  }

  nodeInfo.recycle();
}

This is the current code used by https://github.com/chetbox/android-mouse-cursor.

Android Version is 8.0, stock Android

Is there a better, more reliable way to simulate these touch events from Java? Thanks in advance!

Theo
  • 548
  • 2
  • 8
  • 19
  • 1
    It is possible that there could be refinements to the accessibililty-based approach, as I'm not familiar with the details of that code. However, accessibility and root are your only options in general. – CommonsWare Jun 09 '18 at 15:16
  • Hey @Theo How did you solve the problem? – 7geeky Jul 30 '18 at 00:14
  • 1
    I'd recommend checking out [AccessibilityService#dispatchGesture](https://developer.android.com/reference/android/accessibilityservice/AccessibilityService#dispatchGesture(android.accessibilityservice.GestureDescription,%20android.accessibilityservice.AccessibilityService.GestureResultCallback,%20android.os.Handler)) and the corresponding [GestureDescription](https://developer.android.com/reference/android/accessibilityservice/GestureDescription.StrokeDescription) documentation. They should actually execute raw gestures which won't depend on developers properly implementing onClick. – Jabronie Man Aug 03 '18 at 16:45
  • @CommonsWare, For example, *Team View QuickSupport* today [request **Special Acess**](https://prnt.sc/kk9apj) permission, this meet that any app also requesting this permission will be able to simulate touch events in devices not rooted? –  Aug 18 '18 at 21:59
  • I do not see a permission in the AOSP that would be described as "special access". My guess is that this is something specific that TeamViewer has arranged with the device manufacturer. – CommonsWare Aug 18 '18 at 22:01
  • @CommonsWare, Relative to printscreen that i showed in my last coment, after you click in *OPEN SETTING** the next screen is [this](http://prntscr.com/kk9d8q). –  Aug 18 '18 at 22:05
  • @user9672569: I do not see "Permit special access" anywhere in the AOSP for Android 8.1 or 9.0. – CommonsWare Aug 18 '18 at 22:09
  • @CommonsWare, i had saw [this question](https://stackoverflow.com/questions/17954825/how-does-teamviewer-take-screenshots-and-allow-remote-control-without-root), where says that he have certificates gave by a specific manufacturer. This make some sense in your opinion? –  Aug 18 '18 at 22:11
  • @CommonsWare, `I do not see "Permit special access" anywhere in the AOSP for Android 8.1 or 9.0`. OK, here i'm testing on 7.1 :D –  Aug 18 '18 at 22:12
  • @user9672569: "This make some sense in your opinion?" -- yes, that is how I think that this works. "here i'm testing on 7.1" -- I cannot find that string in that version either. – CommonsWare Aug 18 '18 at 22:13
  • @CommonsWare, OK, thank you by your comments, i also will continue beliving that to normal apps (running in not rooted devices), only is possible with AccessibilityService. Or normal apps ((using adb commands)) running in rooted devices. –  Aug 18 '18 at 22:33

1 Answers1

30

As suggested, the best way to simulate touch events since Nougat (API 24) is by using an accessibility service and the AccessibilityService#dispatchGesture method.

Here is how I did to simulate a single tap event.

// (x, y) in screen coordinates
private static GestureDescription createClick(float x, float y) {
    // for a single tap a duration of 1 ms is enough
    final int DURATION = 1;

    Path clickPath = new Path();
    clickPath.moveTo(x, y);
    GestureDescription.StrokeDescription clickStroke =
            new GestureDescription.StrokeDescription(clickPath, 0, DURATION);
    GestureDescription.Builder clickBuilder = new GestureDescription.Builder();
    clickBuilder.addStroke(clickStroke);
    return clickBuilder.build();
}

// callback invoked either when the gesture has been completed or cancelled
callback = new AccessibilityService.GestureResultCallback() {
    @Override
    public void onCompleted(GestureDescription gestureDescription) {
        super.onCompleted(gestureDescription);
        Log.d(TAG, "gesture completed");
    }

    @Override
    public void onCancelled(GestureDescription gestureDescription) {
        super.onCancelled(gestureDescription);
        Log.d(TAG, "gesture cancelled");
    }
};

// accessibilityService: contains a reference to an accessibility service
// callback: can be null if you don't care about gesture termination
boolean result = accessibilityService.dispatchGesture(createClick(x, y), callback, null);
Log.d(TAG, "Gesture dispatched? " + result);

To perform other gestures, you might find useful the code used for testing the AccessibilityService#dispatchGesture implementation.

EDIT: I link a post in my blog with an introduction to Android accessibility services.

Cesar Mauri
  • 495
  • 6
  • 14
  • 3
    is there a way to implement touch on api level lower than 24. I have noticed that your app "eva_facial_mouse" does perform taps on android phone with api level below 24. – Akash Karnatak Jan 01 '20 at 11:04
  • 4
    @AkashKarnatak, it is possible until to some extent. EVA uses the AccessibilityNodeInfo#performAction method to perform taps. This means you first need to find the right node you want to tap on. This usually involves searching a tree of accessibility nodes starting from the root. However, not all apps provide such a tree being the games, in my experience, the major offenders. Here is how I actually implemented in EVA: https://github.com/cmauri/eva_facial_mouse/blob/a77b50e3104df3bb8ae3b11e23768b5171bd2462/eviacam/src/main/java/com/crea_si/eviacam/a11yservice/AccessibilityAction.java#L328 – Cesar Mauri Jan 03 '20 at 12:53
  • Can you kindly provide few more words about this API? is it an API open to use by any application, that can simulate any form of touch stroke or tap by freely specifying the geometrical and temporal (touch path and velocity at each point in the path) touch paths up to the point of allowing application developers to actually develop accessibility solutions that replace the need to touch the phone's display area? [It might look like that on first look](https://developer.android.com/guide/topics/ui/accessibility/service). – matanster Sep 25 '20 at 13:05
  • 2
    This worked for me thank you! It should be noted the accessibility service xml needs to have "android:canPerformGestures=true" for this to work. – Flyview Jun 03 '21 at 19:22
  • The problem that i see with this option is that, if i am not wrong, you have a limit to make the "addStroke" method, specifically about 10 times. If you go to the source code of the GestureDescription.java, you can see a variable calle MAX_STROKE_COUNT set it to 10, and in the "addStroke" method ask about the variable. This is a problem only if you don't want to make a lot of strokes. – largoWinch Mar 23 '22 at 15:02
  • do we need to request any permission for this ? if so then how can we request the permission(s) ? – Paul Lam Apr 19 '22 at 17:46
  • would it be possible if i send the (x,y) coordinates from another device through a tcp socket, and pass these to simulate a double tap on the same spot ? – fatbringer Jun 16 '22 at 08:43
  • 1
    @fatbringer it may work provided that the server (the app in the device which is receiving the coordinates) is running in the context of an accessibility server – Cesar Mauri Jun 16 '22 at 13:37
  • @CesarMauri im not familiar with accessibility servers. Are you referring to like ease of access kind of settings ? – fatbringer Jun 18 '22 at 14:09
  • 1
    I meant to say "accessibility service", my mistake @fatbringer. – Cesar Mauri Jun 19 '22 at 18:44
  • @CesarMauri aye got it i will go check it out – fatbringer Jun 23 '22 at 04:58
  • Surprisingly this doesn't work with Android system DocumentsUI app (`com.google.android.documentsui`). When you simulate _a tap_ on a folder/file cell, the cell shows the ripple animation effect but the actual action does not occur. Apparently if an app implements its own [`accessibility delegate`](https://android.googlesource.com/platform/packages/apps/DocumentsUI/+/refs/heads/master/src/com/android/documentsui/dirlist/AccessibilityEventRouter.java) it can determine which accessibility action it wants to accept (only `ACTION_CLICK` and `ACTION_LONG_CLICK` in case of DocumentsUI) – Mikalai Daronin Apr 17 '23 at 08:51