Android辅助权限实战之微信自动评论与点赞

Android辅助权限实战之微信自动评论与点赞

当我们把辅助权限玩的比较熟悉 的时候,就可以释放我们的双手做一些有趣的事情了,例如之前网上流传的微信自动抢红包插件,就是使用的这个服务,不过我们今儿讲的是微信自动评论与点赞功能(抢红包的文章网上已经有比较多)。

一、悬浮窗引导开启提示

为了更好的用户体验,我们需要给我们每一步操作一个明确的提示,让用户知道需要做些什么,特别是引导开启系统权限的时候。

关于悬浮窗的开启,之前有写过一篇文章,Android 悬浮窗踩坑体验,里面有介绍关于悬浮窗的开启、权限以及自定义悬浮窗。不过这里我要介绍的是另一种特殊的技巧,在没有开启悬浮窗权限的情况下,用一个特殊的 Activity 来代替悬浮窗。先介绍两个 Activity 在 AndroidManifest 属性:

1、taskAffinity

简单讲一下这个属性的意思:默认情况下,我们启动的 Activity 都是归属于同包名的任务栈里面,但如果配置这个属性,则该 Activity 会在新的任务栈里面(栈名是你配置的)

android:taskAffinity=".guide"

可以通过以下命令去查看当前任务栈的信息:

adb shell dumpsys activity activities

2、excludeFromRecents

当配置这个属性,可以让你的 Activity 不会出现在最近任务列表里面

android:excludeFromRecents="true"

3、配置 Activity 主题是全屏透明

android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"

为什么要配置这两个属性呢? 因为我们不希望这个特殊的Activity出现在最近的使用列表里面,同时配置 taskAffinity 是为了让这个 Activity 在新的任务栈里面,使得它在 finish 的时候,不是回到我们之前启动过的前一个 Activity (并不想影响我们之前的任务栈),这样的做法就能够在其他 App 界面显示我们的 Activity,需要特别说明的的是:启动该 Acitivity 需要配合 Intent.FLAG_ACTIVITY_NEW_TASK 标识启动。代码如下:

<activity
    android:name="com.czc.ui.act.GuideActivity"
    android:taskAffinity=".guide"
    android:excludeFromRecents="true"
    android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
</activity>

完整代码:

public class GuideActivity extends Activity {

    public static void start(Activity act, String message) {
        Intent intent = new Intent(act, GuideActivity.class);
        intent.putExtra("message", message);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        act.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_guide);

        //设置Activity界面大小
        Window window = getWindow();
        window.setGravity(Gravity.LEFT | Gravity.TOP);
        WindowManager.LayoutParams params = window.getAttributes();
        params.x = 0;
        params.y = 0;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        params.height = ScreenUtil.dip2px(80);
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        window.setAttributes(params);

        TextView tvMessage = findViewById(R.id.tv_message);
        tvMessage.setText(getIntent().getStringExtra("message"));

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
            // 5s 后自动关闭提示
                finish();
            }
        }, 5000);
    }
}

3、界面呈现的效果

1.检测到没有【悬浮窗权限】或者【辅助权限】,弹出权限设置页面 PermissionActivity

PermissionActivity

2.跳转系统设置里面的同时【弹出】 GuideActivity

GuideActivity

二、自动化逻辑代码实现

说明:我们通过 Monitor 工具,取到的节点 id ,在微信的每个版本是不一样的 (微信动态生成节点 id,我是通过服务器后台对不同的微信版本,维护一份 id 配置,根据请求的微信版本,返回对应得 id 值),所以本文以微信 v6.7.2 的版本作为例子,如下代码可作参考。

这里只是提供了微信自动点赞与自动评论的示例,当然本人也写了类似于微信自动加好友,自动加附近的人,检测死粉的功能,钉钉自动打卡... 这里只是抛转引玉,大家根据我提供的思路,去实现,由于源码涉及服务器相关操作,不太方便开源,有问题可以加我QQ:1029226002,

{
    "tasks": [
        {
            "nodes": [
                {
                    "action": "scrllor", 
                    "className": "android.widget.ListView", 
                    "id": "com.tencent.mm:id/cno", 
                    "key": "nearHumanList", 
                    "text": "@附近的人列表"
                }, 
                {
                    "className": "android.widget.TextView", 
                    "id": "com.tencent.mm:id/b3i", 
                    "key": "nearHumanListName", 
                    "text": "@附近的人列表名字"
                }, 
                {
                    "className": "android.widget.TextView", 
                    "id": "com.tencent.mm:id/sm", 
                    "key": "detailName", 
                    "text": "@附近的人详情名字"
                }, 
                {
                    "className": "android.widget.ListView", 
                    "id": "com.tencent.mm:id/bcs", 
                    "key": "phoneHumanList", 
                    "text": "@手机联系人列表"
                }, 
                {
                    "className": "android.widget.TextView", 
                    "id": "com.tencent.mm:id/bgl", 
                    "key": "phoneHumanListName", 
                    "text": "@手机联系人列表名字"
                }, 
                {
                    "className": "android.widget.TextView", 
                    "id": "com.tencent.mm:id/bgm", 
                    "key": "phoneHumanListAccount", 
                    "text": "@手机联系人列表昵称"
                }, 
                {
                    "className": "android.widget.TextView", 
                    "id": "com.tencent.mm:id/sm", 
                    "key": "phoneHumandetailName", 
                    "text": "@手机联系人详情名字"
                }, 
                {
                    "className": "android.widget.ListView",
                    "id": "com.tencent.mm:id/doq",
                    "key": "momentList",
                    "text": "@朋友圈列表"
                }
            ],
            "pages": [ ],
            "taskName": "微信脚本",
            "version": "6.7.2"
        }
    ]
}

1、微信朋友圈自动点赞

效果如图(希望我朋友不会打我,这里就不视频打码了,┭┮﹏┭┮),这里面的悬浮窗可以有效阻挡用户操作,只有点击停止之后,才能操作微信界面。

微信朋友圈自动点赞
/**
 * 朋友圈点赞
 * Created by czc on 2018/8/23.
 */
public class LikeStrategy extends BaseStrategy {

    @Override
    protected boolean handleEvent() {
        /**
         * 匹配每个界面,根据当前界面执行相关操作
         * 1、在微信首页点击【发现】按钮
         * 2、然后点击【朋友圈】,进入朋友圈界面
         * 3、滚动朋友圈【动态列表】,对每个动态进行点赞
         */
        if (matchPage(Page.LauncherUI) || matchPage(Page.WxViewPager)) {
            clickFindBtn(getRoot());
            clickCommentTv(getRoot());
        } else if (checkWxScroller(Page.SnsTimeLineUI)) {
            scrollerList(getRoot());
        } else if (matchPage(Page.BaseP)) {

        } else {
            return false;
        }
        return true;
    }


    private void clickFindBtn(AccessibilityNodeInfo root) {
        NodeUtil.findNodeByTextAndClick(root, "发现");
    }

    private void clickCommentTv(AccessibilityNodeInfo root) {
        NodeUtil.findNodeByTextAndClick(root, "朋友圈");
    }


    private void scrollerList(AccessibilityNodeInfo root) {
        //这里的滚动控件对应于朋友圈动态的 ListView 
        AccessibilityNodeInfo scrollerNode = findNodeByTaskNode(root, getNode("momentList"));
        if (scrollerNode == null) {
            Log.e(TAG, "scroller is null");
            return;
        }
        if (scrollerNode != null) {
            final int count = scrollerNode.getChildCount();
            for (int i = 0; i < count; i++) {
                AccessibilityNodeInfo child = scrollerNode.getChild(i);
                if (child != null && child.isVisibleToUser()) {
                    AccessibilityNodeInfo commentNode = NodeUtil.findNodeByFilter(child, new NodeUtil.NodeFilter() {
                        @Override
                        public String text() {
                            return "评论";
                        }

                        @Override
                        public boolean filter(AccessibilityNodeInfo node) {
                            return node.getClassName() != null && node.getClassName().equals(NodeUtil.IMAGE_VIEW)
                                    && node.getContentDescription() != null && node.getContentDescription().toString().equals("评论");
                        }
                    });
                    if (commentNode != null && commentNode.isVisibleToUser()) {
                        NodeUtil.performClick(commentNode);
                        NodeUtil.findNodeByFilterAndClick(root, new NodeUtil.NodeFilter() {
                            @Override
                            public String text() {
                                return "赞";
                            }

                            @Override
                            public boolean filter(AccessibilityNodeInfo node) {
                                return node.getClassName() != null && node.getClassName().equals(NodeUtil.TEXT_VIEW)
                                        && node.getText() != null && node.getText().toString().equals("赞");
                            }
                        });
                    }
                }
                //可见的最后一个 item 
                if (i == count - 1) {
                    //滚动控件是否可以滚动
                    if (scrollerNode.isScrollable()) {
                        NodeUtil.findNodeByFilterAndClick(root, new NodeUtil.NodeFilter() {
                            @Override
                            public String text() {
                                return "评论";
                            }

                            @Override
                            public boolean filter(AccessibilityNodeInfo node) {
                                return node.getClassName() != null && node.getClassName().equals(NodeUtil.TEXT_VIEW)
                                        && node.getText() != null && node.getText().toString().equals("评论");
                            }
                        });
                        
                        //循环滚动
                        performScroll(scrollerNode);
                    }
                }
            }
        }
    }
}

2、微信朋友圈自动评论

效果如图

微信朋友圈自动评论
/**
 * 朋友圈评论
 * Created by czc on 2018/8/23.
 */
public class CommentStrategy extends BaseStrategy {

    @Override
    protected boolean handleEvent() {
        /**
         * 匹配每个界面,根据当前界面执行相关操作
         * 1、在微信首页点击【发现】按钮
         * 2、然后点击【朋友圈】,进入朋友圈界面
         * 3、滚动朋友圈【动态列表】,对每个动态进行评论
         */
        if (matchPage(Page.LauncherUI) || matchPage(Page.WxViewPager)) {
            clickFindBtn(getRoot());
            clickCommentTv(getRoot());
        } else if (checkWxScroller(Page.SnsTimeLineUI)) {
            scrollerList(getRoot());
        } else if (matchPage(Page.BaseP)) {

        } else {
            return false;
        }
        return true;
    }


    private void clickFindBtn(AccessibilityNodeInfo root) {
        NodeUtil.findNodeByTextAndClick(root, "发现");
    }

    private void clickCommentTv(AccessibilityNodeInfo root) {
        NodeUtil.findNodeByTextAndClick(root, "朋友圈");
    }


    private void scrollerList(AccessibilityNodeInfo root) {
        AccessibilityNodeInfo scrollerNode = findNodeByTaskNode(root, getNode("momentList"));
        if (scrollerNode == null) {
            Log.e(TAG, "scroller is null");
            return;
        }
        if (scrollerNode != null) {
            final int count = scrollerNode.getChildCount();
            for (int i = 0; i < count; i++) {
                AccessibilityNodeInfo child = scrollerNode.getChild(i);
                if (child != null && child.isVisibleToUser()) {
                    AccessibilityNodeInfo commentNode = NodeUtil.findNodeByFilter(child, new NodeUtil.NodeFilter() {
                        @Override
                        public String text() {
                            return "评论";
                        }

                        @Override
                        public boolean filter(AccessibilityNodeInfo node) {
                            return node.getClassName() != null && node.getClassName().equals(NodeUtil.IMAGE_VIEW)
                                    && node.getContentDescription() != null && node.getContentDescription().toString().equals("评论");
                        }
                    });
                    if (commentNode != null && commentNode.isVisibleToUser()) {
                        NodeUtil.performClick(commentNode);
                        NodeUtil.findNodeByFilterAndClick(root, new NodeUtil.NodeFilter() {
                            @Override
                            public String text() {
                                return "评论";
                            }

                            @Override
                            public boolean filter(AccessibilityNodeInfo node) {
                                return node.getClassName() != null && node.getClassName().equals(NodeUtil.TEXT_VIEW)
                                        && node.getText() != null && node.getText().toString().equals("评论");
                            }
                        });

                        AccessibilityNodeInfo editNode = NodeUtil.findNodeByFilter(child, new NodeUtil.NodeFilter() {
                            @Override
                            public String text() {
                                return "评论";
                            }

                            @Override
                            public boolean filter(AccessibilityNodeInfo node) {
                                return node.getClassName() != null && node.getClassName().equals(NodeUtil.EDIT_TEXT)
                                        && node.getText() != null && node.getText().toString().equals("评论");
                            }
                        });
                        if (editNode == null) {
                            if (root != null) {
                                editNode = root.findFocus(AccessibilityNodeInfo.FOCUS_INPUT);
                            }
                        }
                        if (editNode != null) {
                            //输入自定义评论内容
                            NodeUtil.performPaste(editNode, "东西不错哦~");
                            //点击【发送】
                            NodeUtil.findNodeByTextAndClick(root, "发送");
                        }
                    }
                }
                if (i == count - 1) {
                    if (scrollerNode.isScrollable()) {
                        performScroll(scrollerNode);
                    }
                }
            }
        }
    }
}

3、相关工具类

工具类里面的代码是自己封装的,不一定都适用,工具类可能存在不足与缺陷,为什么执行操作需要 Sleep 1s ,因为太快的话,有些界面的节点信息还没更新出来,会导致点击失败等情况:

/**
 * Created by czc on 2017/7/3.
 */
public class NodeUtil {
    private static final String TAG = "NodeUtil";
    private static final int millis = 1000;

    public static final String LIST_VIEW = "android.widget.ListView";
    public static final String ABS_LIST_VIEW = "android.widget.AbsListView";
    public static final String SCROLL_VIEW = "android.widget.ScrollView";
    public static final String TEXT_VIEW = "android.widget.TextView";
    public static final String BUTTON = "android.widget.Button";
    public static final String VIEW = "android.view.View";
    public static final String IMAGE_VIEW = "android.widget.ImageView";
    public static final String IMAGE_BUTTON = "android.widget.ImageButton";
    public static final String GRID_VIEW = "android.widget.GridView";
    public static final String EDIT_TEXT = "android.widget.EditText";
    public static final String RELATIVE_LAYOUT = "android.widget.RelativeLayout";
    public static final String LINEAR_LAYOUT = "android.widget.LinearLayout";
    public static final String LINEAR_LAYOUT_COMPAT = "android.support.v7.widget.LinearLayoutCompat";

    public static void sleep(long times) {
        try {
            Thread.sleep(times);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static boolean checkNodeText(AccessibilityNodeInfo nodeInfo) {
        if (nodeInfo == null) {
            return false;
        }
        return !StringUtil.isEmpty(nodeInfo.getText().toString());
    }

    public static boolean checkNodeDes(AccessibilityNodeInfo nodeInfo) {
        if (nodeInfo == null) {
            return false;
        }
        return !StringUtil.isEmpty(nodeInfo.getContentDescription());
    }

    public static boolean performClick(AccessibilityNodeInfo node) {
        AccessibilityNodeInfo clickNode = node;
        if (clickNode == null) {
            return false;
        }
        while (clickNode != null
                && !clickNode.isClickable()) {
            clickNode = clickNode.getParent();
        }
        if (clickNode != null) {
            boolean result = clickNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            sleep();
            return result;
        }
        Log.e(TAG, "clickNode is null");
        return false;
    }

    // root下,通过 adb 命令执行点击
    public static boolean performShellClick(AccessibilityNodeInfo node) {
        AccessibilityNodeInfo clickNode = node;
        if (clickNode == null) {
            return false;
        }
        Rect r = new Rect();
        node.getBoundsInScreen(r);
        if (r.centerX() > ScreenUtils.getScreenWidth(OttUtil.get().getApp())
                || r.centerY() > ScreenUtils.getScreenHeight(OttUtil.get().getApp())
                || r.centerX() <= 0
                || r.centerY() <= 0) {
            return false;
        }
        String cmd = String.format("input tap %d %d", r.centerX(), r.centerY());
        ShellUtils.CommandResult commandResult = ShellUtils.execCmd(cmd, true);
        sleep();
        return commandResult.result != -1;
    }

    // root下,通过 adb 命令执行长按
    public static boolean performShellLongClick(AccessibilityNodeInfo node, long time) {
        AccessibilityNodeInfo clickNode = node;
        if (clickNode == null) {
            return false;
        }
        Rect r = new Rect();
        clickNode.getBoundsInScreen(r);
        int x = r.centerX();
        int y = r.centerY();
        String cmd = String.format("input swipe %d %d %d %d %d", x, y, x, y, time);
        ShellUtils.CommandResult commandResult = ShellUtils.execCmd(cmd, true);
        NodeUtil.sleep(time);
        return commandResult.result != -1;
    }

    // root下,通过 adb 命令执行滚动
    public static boolean performShellScroll() {
        int screenWidth = ScreenUtils.getScreenWidth(OttUtil.get().getApp());
        int screenHeight = ScreenUtils.getScreenHeight(OttUtil.get().getApp());
        String cmd = String.format("input swipe %d %d %d %d %d", screenWidth / 2, (int) (screenHeight * (3 * 1.0f / 5)),screenWidth / 2, screenHeight / 5,  1500L);
        ShellUtils.CommandResult commandResult = ShellUtils.execCmd(cmd, true);
        NodeUtil.sleep(500);
        return commandResult.result != -1;
    }


    public static boolean performScroll(AccessibilityNodeInfo scrollerNode) {
        while (scrollerNode != null && !scrollerNode.isScrollable()) {
            scrollerNode = scrollerNode.getParent();
        }
        if (scrollerNode != null) {
            boolean result = scrollerNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
            sleep();
            return result;
        }
        Log.e(TAG, "scrollerNode is null");
        return false;
    }

    public static boolean performScrollBack(AccessibilityNodeInfo scrollerNode) {
        while (scrollerNode != null && !scrollerNode.isScrollable()) {
            scrollerNode = scrollerNode.getParent();
        }
        if (scrollerNode != null) {
            boolean result = scrollerNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
            sleep();
            return result;
        }
        return false;
    }

    /**
     *  执行粘贴操作(注意:执行之后,会 sleep 1s)
     * @param ct
     * @param node
     * @param text
     * @return
     */
    public static boolean performPaste(Context ct, AccessibilityNodeInfo node, String text) {
        if (node == null || StringUtil.isEmpty(text)) {
            return false;
        }
        boolean result;
        if (Build.VERSION.SDK_INT >= 21) {
            Bundle arguments = new Bundle();
            arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
            result = node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
            sleep();
            return result;
        } else {
            ClipboardManager cm = (ClipboardManager) ct.getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData mClipData = ClipData.newPlainText("text", text);
            cm.setPrimaryClip(mClipData);
            result = node.performAction(AccessibilityNodeInfo.ACTION_PASTE);
            sleep();
            return result;
        }
    }

    public static boolean hasNode(AccessibilityNodeInfo root, String text) {
        if (root == null || StringUtil.isEmpty(text)) {
            return false;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByText(text);

        if (nodeList == null || nodeList.isEmpty()) {
            return false;
        }
        return true;
    }

    public static AccessibilityNodeInfo findNodeByFilter(AccessibilityNodeInfo root, String text, NodeFilter filter) {
        if (root == null || StringUtil.isEmpty(text)) {
            return null;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByText(text);

        if (nodeList == null || nodeList.isEmpty()) {
            return null;
        }
        AccessibilityNodeInfo clickNode = null;
        for (AccessibilityNodeInfo nodeInfo : nodeList) {
            if (filter.filter(nodeInfo)) {
                clickNode = nodeInfo;
                break;
            }
        }
        return clickNode;
    }
       public static AccessibilityNodeInfo findNodeByFilter(AccessibilityNodeInfo root, NodeTextFilter filter) {
        if (root == null || filter == null || StringUtil.isEmpty(filter.fiterText())) {
            return null;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByText(filter.fiterText());

        if (nodeList == null || nodeList.isEmpty()) {
            return null;
        }
        AccessibilityNodeInfo clickNode = null;
        for (AccessibilityNodeInfo nodeInfo : nodeList) {
            if (filter.filter(nodeInfo)) {
                clickNode = nodeInfo;
                break;
            }
        }
        return clickNode;
    }

    public static AccessibilityNodeInfo findNodeByFilter(AccessibilityNodeInfo root, NodeIdFilter filter) {
        if (root == null || filter == null || StringUtil.isEmpty(filter.fiterViewId())) {
            return null;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByViewId(filter.fiterViewId());

        if (nodeList == null || nodeList.isEmpty()) {
            return null;
        }
        AccessibilityNodeInfo clickNode = null;
        for (AccessibilityNodeInfo nodeInfo : nodeList) {
            if (filter.filter(nodeInfo)) {
                clickNode = nodeInfo;
                break;
            }
        }
        return clickNode;
    }


    public static AccessibilityNodeInfo findNodeByText(AccessibilityNodeInfo root, String text) {
        if (root == null || StringUtil.isEmpty(text)) {
            return null;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByText(text);

        if (nodeList == null || nodeList.isEmpty()) {
            return null;
        }
        AccessibilityNodeInfo clickNode = null;
        for (AccessibilityNodeInfo nodeInfo : nodeList) {
            boolean eqText = nodeInfo.getText() != null && nodeInfo.getText().toString().equals(text);
            boolean eqDesc = nodeInfo.getContentDescription() != null && nodeInfo.getContentDescription().toString().equals(text);
            if (eqText || eqDesc) {
                clickNode = nodeInfo;
                break;
            }
        }
        return clickNode;
    }

    public static AccessibilityNodeInfo findNodeContainsText(AccessibilityNodeInfo root, String text) {
        if (root == null || StringUtil.isEmpty(text)) {
            return null;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByText(text);

        if (nodeList == null || nodeList.isEmpty()) {
            return null;
        }
        AccessibilityNodeInfo clickNode = null;
        for (AccessibilityNodeInfo nodeInfo : nodeList) {
            boolean eqText = nodeInfo.getText() != null && nodeInfo.getText().toString().contains(text);
            boolean eqDesc = nodeInfo.getContentDescription() != null && nodeInfo.getContentDescription().toString().contains(text);
            if (eqText || eqDesc) {
                clickNode = nodeInfo;
                break;
            }
        }
        return clickNode;
    }

    public static boolean findNodeByTextAndClick(AccessibilityNodeInfo root, String text) {
        if (root == null || StringUtil.isEmpty(text)) {
            return false;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByText(text);

        if (nodeList == null || nodeList.isEmpty()) {
            return false;
        }
        AccessibilityNodeInfo clickNode = null;
        for (AccessibilityNodeInfo nodeInfo : nodeList) {
            boolean eqText = nodeInfo.getText() != null && nodeInfo.getText().toString().equals(text);
            boolean eqDesc = nodeInfo.getContentDescription() != null && nodeInfo.getContentDescription().toString().equals(text);
            if (eqText || eqDesc) {
                clickNode = nodeInfo;
                break;
            }
        }
        return performClick(clickNode);
    }

    public static boolean findNodeByIdAndClick(AccessibilityNodeInfo root, String id) {
        if (root == null || StringUtil.isEmpty(id)) {
            return false;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByViewId(id);

        if (nodeList == null || nodeList.isEmpty()) {
            return false;
        }
        return performClick(nodeList.get(0));
    }

    public static boolean findNodeContainsTextAndClick(AccessibilityNodeInfo root, String text) {
        if (root == null || StringUtil.isEmpty(text)) {
            return false;
        }
        List<AccessibilityNodeInfo> nodeList = root.findAccessibilityNodeInfosByText(text);

        if (nodeList == null || nodeList.isEmpty()) {
            return false;
        }
        AccessibilityNodeInfo clickNode = null;
        for (AccessibilityNodeInfo nodeInfo : nodeList) {
            boolean eqText = nodeInfo.getText() != null && nodeInfo.getText().toString().contains(text);
            boolean eqDesc = nodeInfo.getContentDescription() != null && nodeInfo.getContentDescription().toString().contains(text);
            if (eqText || eqDesc) {
                clickNode = nodeInfo;
                break;
            }
        }
        Log.i(TAG, "点击:"+text+"!");
        return performClick(clickNode);
    }

    public static boolean findNodeByIdTextAndClick(AccessibilityNodeInfo root, String id, String text, boolean isNewPage) {
        AccessibilityNodeInfo clickNode = findNodeByIdAndText(root, id, text);
        if (clickNode == null) {
            return false;
        }
        return performClick(clickNode);
    }

    public static boolean findNodeByIdClassAndClick(AccessibilityNodeInfo root, String id, String className, boolean isNewPage) {
        AccessibilityNodeInfo clickNode = findNodeByIdAndClassName(root, id, className);
        if (clickNode == null) {
            return false;
        }
        return performClick(clickNode);
    }


    public static AccessibilityNodeInfo findNodeByIdAndClassName(AccessibilityNodeInfo root, String id, String className) {
        if (root == null) {
            return null;
        }
        List<AccessibilityNodeInfo> idNodeInfoList = root.findAccessibilityNodeInfosByViewId(id);
        if (idNodeInfoList == null || idNodeInfoList.isEmpty()) {
            return null;
        }
        for (int i = 0; i < idNodeInfoList.size(); i++) {
            AccessibilityNodeInfo nodeInfo = idNodeInfoList.get(i);
            if (nodeInfo == null) {
                continue;
            }
            //根据className过滤
            if (!StringUtil.isEmpty(className)) {
                if (className.equals(nodeInfo.getClassName())) {
                    return nodeInfo;
                }
            }
        }
        return null;
    }

    public static AccessibilityNodeInfo findNodeByTextAndClass(AccessibilityNodeInfo root, String text, String clazz) {
        if (root == null) {
            return null;
        }
        List<AccessibilityNodeInfo> idNodeInfoList = root.findAccessibilityNodeInfosByText(text);
        if (idNodeInfoList == null || idNodeInfoList.isEmpty()) {
            return null;
        }

        AccessibilityNodeInfo clickNode = null;
        for (int i = 0; i < idNodeInfoList.size(); i++) {
            AccessibilityNodeInfo nodeInfo = idNodeInfoList.get(i);
            if (nodeInfo == null) {
                continue;
            }
            //根据class过滤
            if (!StringUtil.isEmpty(clazz)) {
                if (clazz.equals(nodeInfo.getClassName().toString())) {
                    clickNode = nodeInfo;
                    break;
                }
            }
        }
        if (clickNode == null) {
            return null;
        }
        return clickNode;
    }

    public static AccessibilityNodeInfo findNodeByIdAndText(AccessibilityNodeInfo root, String id, String text) {
        if (root == null) {
            return null;
        }
        List<AccessibilityNodeInfo> idNodeInfoList = root.findAccessibilityNodeInfosByViewId(id);
        if (idNodeInfoList == null || idNodeInfoList.isEmpty()) {
            return null;
        }

        AccessibilityNodeInfo clickNode = null;
        for (int i = 0; i < idNodeInfoList.size(); i++) {
            AccessibilityNodeInfo nodeInfo = idNodeInfoList.get(i);
            if (nodeInfo == null) {
                continue;
            }
            //根据text过滤
            if (!StringUtil.isEmpty(nodeInfo.getText())
                    && !StringUtil.isEmpty(text)) {
                if (text.equals(nodeInfo.getText())) {
                    clickNode = nodeInfo;
                    break;
                }
            }
        }
        if (clickNode == null) {
            return null;
        }
        return clickNode;
    }

    /**
     * @param root
     * @param id
     * @param className
     * @return
     */
    public static List<AccessibilityNodeInfo> findNodeByIdAndClassNameList(AccessibilityNodeInfo root, String id, String className) {
        if (root == null) {
            return null;
        }
        List<AccessibilityNodeInfo> resultList = new ArrayList<>();
        List<AccessibilityNodeInfo> idNodeInfoList = root.findAccessibilityNodeInfosByViewId(id);
        if (idNodeInfoList == null || idNodeInfoList.isEmpty()) {
            return null;
        }
        for (int i = 0; i < idNodeInfoList.size(); i++) {
            AccessibilityNodeInfo nodeInfo = idNodeInfoList.get(i);
            if (nodeInfo == null) {
                continue;
            }
            //根据className过滤
            if (!StringUtil.isEmpty(className)) {
                if (className.equals(nodeInfo.getClassName())) {
                    resultList.add(nodeInfo);
                }
            }
        }
        return resultList;
    }

    public static AccessibilityNodeInfo findNodeByClass(AccessibilityNodeInfo root, String className) {
        if (TextUtils.isEmpty(className) || root == null) {
            return null;
        }
        int childCount = root.getChildCount();
        for (int i = 0; i < childCount; i++) {
            AccessibilityNodeInfo rootChild = root.getChild(i);
            if (rootChild != null) {
                if (className.equals(rootChild.getClassName().toString().trim())) {
                    return rootChild;
                }
            }
        }
        return null;
    }

    public static List<AccessibilityNodeInfo> findNodeByClassList(AccessibilityNodeInfo root, String className) {
        List<AccessibilityNodeInfo> list = new ArrayList<>();
        if (TextUtils.isEmpty(className) || root == null) {
            return Collections.EMPTY_LIST;
        }
        int childCount = root.getChildCount();
        for (int i = 0; i < childCount; i++) {
            AccessibilityNodeInfo rootChild = root.getChild(i);
            if (rootChild != null) {
                if (className.equals(rootChild.getClassName().toString().trim())) {
                    list.add(rootChild);
                }
            }
        }
        return list;
    }

    public static List<AccessibilityNodeInfo> traverseNodefilterByDesc(AccessibilityNodeInfo root, final String desc) {
        if (TextUtils.isEmpty(desc) || root == null) {
            return null;
        }
        List<AccessibilityNodeInfo> list = new ArrayList<>();
        traverseNodeClassToList(root, list, new NodeFilter() {
            @Override
            public boolean filter(AccessibilityNodeInfo node) {
                return node.getContentDescription() != null && desc.equals(node.getContentDescription().toString());
            }
        });
        return list;
    }

    public static List<AccessibilityNodeInfo> traverseNodeByClassName(AccessibilityNodeInfo root, final String className) {
        if (TextUtils.isEmpty(className) || root == null) {
            return Collections.EMPTY_LIST;
        }
        List<AccessibilityNodeInfo> list = new ArrayList<>();
        traverseNodeClassToList(root, list, new NodeFilter() {
            @Override
            public boolean filter(AccessibilityNodeInfo node) {
                return node.getClassName() != null && className.equals(node.getClassName().toString());
            }
        });
        return list;
    }

    private static void traverseNodeClassToList(AccessibilityNodeInfo node, List<AccessibilityNodeInfo> list, NodeFilter filter) {
        if (node == null || node.getChildCount() == 0) {
            return;
        }
        for (int i = 0; i < node.getChildCount(); i++) {
            AccessibilityNodeInfo child = node.getChild(i);
            if (child != null) {
                if (filter.filter(child)) {
                    list.add(child);
                }
                if (child.getChildCount() > 0) {
                    traverseNodeClassToList(child, list, filter);
                }
            }
        }
    }

    public interface NodeFilter {
        boolean filter(AccessibilityNodeInfo node);
    }

    public interface NodeTextFilter extends NodeFilter {
        String fiterText();
    }

    public interface NodeIdFilter extends NodeFilter {
        String fiterViewId();
    }
}

在部分 Root 的手机,我们可以通过辅助权限找到该节点在屏幕上的位置,并获取中心点:

Rect r = new Rect();
node.getBoundsInScreen(r);
if (r.centerX() > ScreenUtils.getScreenWidth(OttUtil.get().getApp())
        || r.centerY() > ScreenUtils.getScreenHeight(OttUtil.get().getApp())
        || r.centerX() <= 0
        || r.centerY() <= 0) {
    return false;
}
String cmd = String.format("input tap %d %d", r.centerX(), r.centerY());

再去执行相关操作:

ShellUtils.CommandResult commandResult = ShellUtils.execCmd(cmd, true);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容