Android輔助權限實戰(zhàn)之微信自動評論與點贊

Android輔助權限實戰(zhàn)之微信自動評論與點贊

當我們把輔助權限玩的比較熟悉 的時候三圆,就可以釋放我們的雙手做一些有趣的事情了券盅,例如之前網(wǎng)上流傳的微信自動搶紅包插件钾唬,就是使用的這個服務十办,不過我們今兒講的是微信自動評論與點贊功能(搶紅包的文章網(wǎng)上已經(jīng)有比較多)。

一嗅绰、懸浮窗引導開啟提示

為了更好的用戶體驗舍肠,我們需要給我們每一步操作一個明確的提示,讓用戶知道需要做些什么办陷,特別是引導開啟系統(tǒng)權限的時候貌夕。

關于懸浮窗的開啟,之前有寫過一篇文章民镜,Android 懸浮窗踩坑體驗啡专,里面有介紹關于懸浮窗的開啟、權限以及自定義懸浮窗制圈。不過這里我要介紹的是另一種特殊的技巧们童,在沒有開啟懸浮窗權限的情況下畔况,用一個特殊的 Activity 來代替懸浮窗。先介紹兩個 Activity 在 AndroidManifest 屬性:

1慧库、taskAffinity

簡單講一下這個屬性的意思:默認情況下跷跪,我們啟動的 Activity 都是歸屬于同包名的任務棧里面,但如果配置這個屬性齐板,則該 Activity 會在新的任務棧里面(棧名是你配置的)

android:taskAffinity=".guide"

可以通過以下命令去查看當前任務棧的信息:

adb shell dumpsys activity activities

2吵瞻、excludeFromRecents

當配置這個屬性,可以讓你的 Activity 不會出現(xiàn)在最近任務列表里面

android:excludeFromRecents="true"

3甘磨、配置 Activity 主題是全屏透明

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

為什么要配置這兩個屬性呢橡羞? 因為我們不希望這個特殊的Activity出現(xiàn)在最近的使用列表里面,同時配置 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、界面呈現(xiàn)的效果

1.檢測到?jīng)]有【懸浮窗權限】或者【輔助權限】椎侠,彈出權限設置頁面 PermissionActivity

PermissionActivity

2.跳轉(zhuǎn)系統(tǒng)設置里面的同時【彈出】 GuideActivity

GuideActivity

二第租、自動化邏輯代碼實現(xiàn)

說明:我們通過 Monitor 工具,取到的節(jié)點 id 肺蔚,在微信的每個版本是不一樣的 (微信動態(tài)生成節(jié)點 id煌妈,我是通過服務器后臺對不同的微信版本儡羔,維護一份 id 配置宣羊,根據(jù)請求的微信版本,返回對應得 id 值)汰蜘,所以本文以微信 v6.7.2 的版本作為例子仇冯,如下代碼可作參考。

這里只是提供了微信自動點贊與自動評論的示例族操,當然本人也寫了類似于微信自動加好友苛坚,自動加附近的人,檢測死粉的功能色难,釘釘自動打卡... 這里只是拋轉(zhuǎn)引玉泼舱,大家根據(jù)我提供的思路,去實現(xiàn)枷莉,由于源碼涉及服務器相關操作娇昙,不太方便開源,有問題可以加我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": "@手機聯(lián)系人列表"
                }, 
                {
                    "className": "android.widget.TextView", 
                    "id": "com.tencent.mm:id/bgl", 
                    "key": "phoneHumanListName", 
                    "text": "@手機聯(lián)系人列表名字"
                }, 
                {
                    "className": "android.widget.TextView", 
                    "id": "com.tencent.mm:id/bgm", 
                    "key": "phoneHumanListAccount", 
                    "text": "@手機聯(lián)系人列表昵稱"
                }, 
                {
                    "className": "android.widget.TextView", 
                    "id": "com.tencent.mm:id/sm", 
                    "key": "phoneHumandetailName", 
                    "text": "@手機聯(lián)系人詳情名字"
                }, 
                {
                    "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() {
        /**
         * 匹配每個界面祭陷,根據(jù)當前界面執(zhí)行相關操作
         * 1、在微信首頁點擊【發(fā)現(xiàn)】按鈕
         * 2趣席、然后點擊【朋友圈】颗胡,進入朋友圈界面
         * 3、滾動朋友圈【動態(tài)列表】吩坝,對每個動態(tài)進行點贊
         */
        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, "發(fā)現(xiàn)");
    }

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


    private void scrollerList(AccessibilityNodeInfo root) {
        //這里的滾動控件對應于朋友圈動態(tài)的 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("評論");
                            }
                        });
                        
                        //循環(huán)滾動
                        performScroll(scrollerNode);
                    }
                }
            }
        }
    }
}

2毒姨、微信朋友圈自動評論

效果如圖

微信朋友圈自動評論
/**
 * 朋友圈評論
 * Created by czc on 2018/8/23.
 */
public class CommentStrategy extends BaseStrategy {

    @Override
    protected boolean handleEvent() {
        /**
         * 匹配每個界面,根據(jù)當前界面執(zhí)行相關操作
         * 1钉寝、在微信首頁點擊【發(fā)現(xiàn)】按鈕
         * 2弧呐、然后點擊【朋友圈】,進入朋友圈界面
         * 3嵌纲、滾動朋友圈【動態(tài)列表】俘枫,對每個動態(tài)進行評論
         */
        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, "發(fā)現(xiàn)");
    }

    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) {
                            //輸入自定義評論內(nèi)容
                            NodeUtil.performPaste(editNode, "東西不錯哦~");
                            //點擊【發(fā)送】
                            NodeUtil.findNodeByTextAndClick(root, "發(fā)送");
                        }
                    }
                }
                if (i == count - 1) {
                    if (scrollerNode.isScrollable()) {
                        performScroll(scrollerNode);
                    }
                }
            }
        }
    }
}

3、相關工具類

工具類里面的代碼是自己封裝的逮走,不一定都適用鸠蚪,工具類可能存在不足與缺陷,為什么執(zhí)行操作需要 Sleep 1s 师溅,因為太快的話茅信,有些界面的節(jié)點信息還沒更新出來,會導致點擊失敗等情況:

/**
 * 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 命令執(zhí)行點擊
    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 命令執(zhí)行長按
    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 命令執(zhí)行滾動
    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;
    }

    /**
     *  執(zhí)行粘貼操作(注意:執(zhí)行之后窿锉,會 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;
            }
            //根據(jù)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;
            }
            //根據(jù)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;
            }
            //根據(jù)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;
            }
            //根據(jù)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 的手機,我們可以通過輔助權限找到該節(jié)點在屏幕上的位置嗡载,并獲取中心點:

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());

再去執(zhí)行相關操作:

ShellUtils.CommandResult commandResult = ShellUtils.execCmd(cmd, true);
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窑多,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子洼滚,更是在濱河造成了極大的恐慌埂息,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耿芹,居然都是意外死亡崭篡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門吧秕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琉闪,“玉大人,你說我怎么就攤上這事砸彬〉弑校” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵砂碉,是天一觀的道長蛀蜜。 經(jīng)常有香客問我,道長增蹭,這世上最難降的妖魔是什么滴某? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮滋迈,結(jié)果婚禮上霎奢,老公的妹妹穿的比我還像新娘。我一直安慰自己饼灿,他們只是感情好幕侠,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碍彭,像睡著了一般晤硕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上庇忌,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天舞箍,我揣著相機與錄音,去河邊找鬼漆枚。 笑死创译,一個胖子當著我的面吹牛抵知,可吹牛的內(nèi)容都是我干的墙基。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刷喜,長吁一口氣:“原來是場噩夢啊……” “哼残制!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掖疮,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤初茶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后浊闪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恼布,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡螺戳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了折汞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倔幼。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖爽待,靈堂內(nèi)的尸體忽然破棺而出损同,到底是詐尸還是另有隱情,我是刑警寧澤鸟款,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布膏燃,位于F島的核電站,受9級特大地震影響何什,放射性物質(zhì)發(fā)生泄漏组哩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一处渣、第九天 我趴在偏房一處隱蔽的房頂上張望禁炒。 院中可真熱鬧,春花似錦霍比、人聲如沸幕袱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽们豌。三九已至,卻和暖如春浅妆,著一層夾襖步出監(jiān)牢的瞬間望迎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工凌外, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辩尊,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓康辑,卻偏偏與公主長得像摄欲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子疮薇,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容