Is it possible to use adb commands to click on a view by finding its ID?

5.7k views Asked by At

Suppose I have an app (in debug/release build , made by me or not), which has an ID for a specific view.

Is it possible to call adb command to click on this view?

I know it's possible to make it click on a specific coordinate, but is it possible to use the ID instead ?

I ask this because I do know that the "Layout Inspector" tool (available via Android Studio) and the "View hierarchy" tool (available via "Android Device Monitor", previously used via DDMS) can show the ids of the views (and even their coordinates and bounding box), so maybe it can be a better way to simulate touches when performing some automatic tests.

I can use a rooted method if needed.


EDIT: I've set a bounty in case there is an easier/better way than what I've written in my own answer, which was to parse the result of "adb shell dumpsys activity top" .

I would like to know if it's possible to get the views coordinates (and sizes of course) that are shown on the screen, including as much information about them (to identify each). This should be possible via the device too. Maybe something that has the same output data of what's available from the "monitor" tool :

enter image description here

Notice how it can get the basic information of the views, including the text, the id, and the bounds of each.

As I've read, this might be possible via AccessibilityService, but sadly I can't understand how it all works, what are its capabilities, how to trigger it, what are its requirements, etc...

1

There are 1 answers

12
android developer On BEST ANSWER

Using what @pskink explained in the comments above, here's how I achieved this:

First, I ran this command:

adb shell dumpsys activity top

Then, I used this code to parse it:

public class ViewCoordsGetter {
    public static Rect getViewBoundyingBox(String viewIdStr) {
        final List<String> viewHierarchyLog = //result of the command
        for (int i = 0; i < viewHierarchyLog.size(); ++i) {
            String line = viewHierarchyLog.get(i);
            if (line.contains(":id/" + viewIdStr + "}")) {
                Rect result = getBoundingBoxFromLine(line);
                if (i == 0)
                    return result;
                int currentLineStart = getStartOfViewDetailsInLine(line);
                for (int j = i - 1; j >= 0; --j) {
                    line = viewHierarchyLog.get(j);
                    if ("View Hierarchy:".equals(line.trim()))
                        break;
                    int newLineStart = getStartOfViewDetailsInLine(line);
                    if (newLineStart < currentLineStart) {
                        final Rect boundingBoxFromLine = getBoundingBoxFromLine(line);
                        result.left += boundingBoxFromLine.left;
                        result.right += boundingBoxFromLine.left;
                        result.top += boundingBoxFromLine.top;
                        result.bottom += boundingBoxFromLine.top;
                        currentLineStart = newLineStart;
                    }
                }
                return result;
            }
        }
        return null;
    }

    private static int getStartOfViewDetailsInLine(String s) {
        int i = 0;
        while (true)
            if (s.charAt(i++) != ' ')
                return --i;
    }

    private static Rect getBoundingBoxFromLine(String line) {
        int endIndex = line.indexOf(',', 0);
        int startIndex = endIndex - 1;
        while (!Character.isSpaceChar(line.charAt(startIndex - 1)))
            --startIndex;
        int left = Integer.parseInt(line.substring(startIndex, endIndex));
        startIndex = endIndex + 1;
        endIndex = line.indexOf('-', startIndex);
        endIndex = line.charAt(endIndex - 1) == ',' ? line.indexOf('-', endIndex + 1) : endIndex;
        int top = Integer.parseInt(line.substring(startIndex, endIndex));
        startIndex = endIndex + 1;
        endIndex = line.indexOf(',', startIndex);
        int right = Integer.parseInt(line.substring(startIndex, endIndex));
        startIndex = endIndex + 1;
        //noinspection StatementWithEmptyBody
        for (endIndex = startIndex + 1; Character.isDigit(line.charAt(endIndex)); ++endIndex)
            ;
        int bot = Integer.parseInt(line.substring(startIndex, endIndex));
        return new Rect(left, top, right, bot);
    }
}