javaandroidxmlaccessibilityservice

dispatchGesture always returns false


My service cannot initiate a tap on the screen. dispatchGesture from AccessibilityService always returns false. How to solve this problem?

SDK API 31

My service:

public class FloatingButton extends AccessibilityService {
    private WindowManager windowManager;
    private ImageView shotButton;
    private int[2] passMark = {100, 100};
    private int[2] shotMark = {200, 200};

    @Override
    public void onInterrupt() {}

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {}

    @Override
    public void onCreate() {
        super.onCreate();

        shotButton = new ImageView(this);

        shotButton.setImageResource(R.drawable.activate);

        windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
        final LayoutParams shotButtonParams = getLayoutParams();
        shotButtonParams.x = 0;
        shotButtonParams.y = 100;

        windowManager.addView(shotButton, shotButtonParams);

        try {
            OnTouchListener shotButtonOnTouchListener = getShotButtonOnTouchListener(shotButtonParams);
            OnClickListener shotButtonOnClickListener = getShotButtonOnClickListener();

            shotButton.setOnClickListener(shotButtonOnClickListener);
            shotButton.setOnTouchListener(shotButtonOnTouchListener);
        } catch (Exception exception) {
            Log.e("SuperShot", exception.getMessage());
        }
    }

    private LayoutParams getLayoutParams() {
        final LayoutParams layoutParams = new WindowManager.LayoutParams(
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT,
            LayoutParams.TYPE_APPLICATION_OVERLAY,
            LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT
        );

        layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
        return layoutParams;
    }

    private OnClickListener getShotButtonOnClickListener() {
        return new OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d("SuperShot", "Coordinate pass: " + String.valueOf(passMark[0]) + ", " + String.valueOf(passMark[1]));
                Log.d("SuperShot", "Coordinate shot: " + String.valueOf(shotMark[0]) + ", " + String.valueOf(shotMark[1]));

                click(passMark[0], passMark[1]);
            }
        };
    }

    private void click(int x, int y) {
        Path path = new Path();
        path.moveTo(x, y);

        StrokeDescription clickStroke = new StrokeDescription(path, 0, 10);
        Builder clickBuilder = new Builder();
        clickBuilder.addStroke(clickStroke);
        GestureDescription gesture = clickBuilder.build();
        boolean result = this.dispatchGesture(gesture, null, null);
        Log.d("SuperShot", String.valueOf(result));
    }
}

The logs of the click method show false, which means that dispatchGesture was unable to complete the click. How can this problem be solved and what could it be related to?

Manifest.xml:

<service
    android:name="com.example.shots.FloatingButton"
    android:foregroundServiceType="specialUse"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
    android:exported="false">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>
    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/config_accessibility_service" />
</service>

config_accessibility_service.xml:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault"
    android:canPerformGestures="true" />

Solution

  • There is nothing wrong with your code, I think this is not working due to not granting permission properly. For granting Accessibility service permission is launch the Accessibility settings page and get the user to enable it:

    Intent openSettings = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
    openSettings.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
    Intent.FLAG_ACTIVITY_NO_HISTORY);
    startActivity(openSettings);
    

    Correct me if I wrong.