利用AccessibilityServicer查找节点实现模拟点击,直接打开微信朋友圈
继上一篇 [https://blog.csdn.net/weixin_40112993/article/details/89246258] 了解到AccessibilityService基本用法之后,我们知道在使用时需要 自定义一个类 MyAccessibilityService 继承 AccessibilityService ,然后复写其中的两个方法,onAccessibilityEvent() and onInterrupt(),在 onAccessibilityEvent 中我们可以监听界面的变化,继而查找节点,做相应事件。
直接上代码:
在 MainActivity.java 中写进入微信App的的流程:
package com.huawei.myaccessibilityservice; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.provider.Settings; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; public class MainActivity extends AppCompatActivity { public static String packageName = "com.tencent.mm"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_open_service).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } }); findViewById(R.id.btn_open_friend_circle).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startOpenFriendCircle(); } }); } /** * 開始正常流程 */ private void startOpenFriendCircle() { if (!Utils.isAppAvilible(MainActivity.this, packageName)) { //没有安装微信 Toast.makeText(this, "未安装微信", Toast.LENGTH_LONG).show(); } else { //检测用户是否开启辅助功能权限,如果沒有則進入設置界面開啟輔助 if (Utils.isNeededPermissionsGranted(MainActivity.this)) { //開始正常流程。。。 startApp(packageName); } } } /** * 進入APP * @param packageName 包名 */ private void startApp(String packageName) { PackageManager packageManager = getPackageManager(); Intent intent = packageManager.getLaunchIntentForPackage(packageName); if (intent == null) { return; } intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } }
从代码中可以看到在进入微信APP之前我们先判断是否安装微信,如果安装再判断无障碍服务是否打开,如果打开直接进入微信,如果没有则先进入无障碍服务页面打开服务。
其中Utils工具类为:
package com.huawei.myaccessibilityservice; import android.annotation.SuppressLint; import android.app.Application; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import java.lang.reflect.InvocationTargetException; import java.util.List; /** * 工具类 */ public class Utils { /** * Check whether an application is installed * * @param context context * @param packageName the name of package */ public static boolean isAppAvilible(Context context, String packageName) { final PackageManager packageManager = context.getPackageManager();// 獲取packagemanager List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);//獲取所有已安裝程序的包信息 if (pinfo != null) { int size = pinfo.size(); for (int i = 0; i < size; i++) { String str = pinfo.get(i).packageName; if (str.equals(packageName)) { return true; } } } return false; } /** * Return whether service is running. * * @return {@code true}: yes<br>{@code false}: no */ public static boolean isNeededPermissionsGranted(Context context) { boolean accessibilityGranted = isAccessibilitySettingsOn(context); if (!accessibilityGranted) { //判斷輔助功能是否打開 如果沒有打開 則跳轉至輔助功能界面 Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); context.startActivity(intent); return false; } return true; } private static boolean isAccessibilitySettingsOn(Context mContext) { int accessibilityEnabled = 0; final String service = Utils.getApp().getPackageName() + "/" + MyAccessibilityService.class.getCanonicalName(); try { accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(), android.provider.Settings.Secure.ACCESSIBILITY_ENABLED); } catch (Settings.SettingNotFoundException e) { Log.e("e", "Error finding setting, default accessibility to not found: " + e.getMessage()); } TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); if (accessibilityEnabled == 1) { String settingValue = Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); if (settingValue != null) { mStringColonSplitter.setString(settingValue); while (mStringColonSplitter.hasNext()) { String accessibilityService = mStringColonSplitter.next(); if (accessibilityService.equalsIgnoreCase(service)) { return true; } } } } return false; } /** * Return the context of Application object. * * @return the context of Application object */ public static Application getApp() { Application app = getApplicationByReflect(); return app; } private static Application getApplicationByReflect() { try { @SuppressLint("PrivateApi") Class<?> activityThread = Class.forName("android.app.ActivityThread"); Object thread = activityThread.getMethod 4000 ("currentActivityThread").invoke(null); Object app = activityThread.getMethod("getApplication").invoke(thread); if (app == null) { throw new NullPointerException("u should init first"); } return (Application) app; } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException e) { Log.e("e", e.toString()); } throw new NullPointerException("u should init first"); } }
打开了无障碍服务 后,我们自定义的 MyAccessibilityService 就开始工作了,然后进入微信APP 获取节点并进行模拟点击,打开朋友圈。
MyAccessibilityService.java代码为:
package com.huawei.myaccessibilityservice; import android.accessibilityservice.AccessibilityService; import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; /** * custom service */ public class MyAccessibilityService extends AccessibilityService { private static MyAccessibilityService instance; private static void setInstance(MyAccessibilityService service) { instance = service; } public static synchronized MyAccessibilityService getInstance() { return instance; } @Override public void onCreate() { super.onCreate(); setInstance(this); } @Override public void onDestroy() { super.onDestroy(); setInstance(null); } @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event == null) { return; } String pkgName = event.getPackageName() != null ? event.getPackageName().toString() : ""; String clsName = event.getClassName() != null ? event.getClassName().toString() : ""; String desc = event.getContentDescription() != null ? event.getContentDescription().toString() : ""; String kText = event.getText() != null ? event.getText().toString() : ""; Log.e("---tag--", "type=" + event.getEventType() + ",---clsName--" + clsName + ",---desc--" + desc + ",---kText--" + kText); if (!pkgName.equals(MainActivity.packageName)) { return; } AccessibilityNodeInfo nodeInfo = event.getSource(); if (nodeInfo == null) { return; } if (pkgName.equals(MainActivity.packageName)) { if (clsName.equals(BaseContact.clsName_Main) && event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { setNodeClick(nodeInfo, BaseContact.text_find); } else if (clsName.equals(BaseContact.clsName_listView) && event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { setNodeClick(nodeInfo, BaseContact.text_friend_circle); } } } @Override public void onInterrupt() { } /** * Auto-click after finding the node * * @param nodeInfo AccessibilityNodeInfo * @param text the message of node */ private void setNodeClick(AccessibilityNodeInfo nodeInfo, String text) { AccessibilityNodeInfo info = NodeUtils.findNodeByParentClick(nodeInfo, text); if (info != null) { info.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } }
在代码中看到有个NodeUtils类,这个类就是用来寻找节点的。
NodeUtils.java 代码为:
package com.huawei.myaccessibilityservice; import android.util.Log; import android.view.accessibility.AccessibilityNodeInfo; import java.util.List; /** * find node tools */ public class NodeUtils { /** * find node by classname * * @param nodeInfo AccessibilityNodeInfo * @param className classname * @return node */ public static AccessibilityNodeInfo findNodeInfosByClassName(AccessibilityNodeInfo nodeInfo, String className) { if (nodeInfo == null) { nodeInfo = MyAccessibilityService.getInstance().getRootInActiveWindow(); if (nodeInfo == null) { return null; } } int size = nodeInfo.getChildCount(); for (int i = 0; i < size; i++) { AccessibilityNodeInfo childNode = nodeInfo.getChild(i); if (childNode != null) { String childClassName = (childNode.getClassName() != null) ? childNode.getClassName().toString() : ""; if (childClassName.equals(className)) { return childNode; } AccessibilityNodeInfo grandNode = findNodeInfosByClassName(childNode, className); if (grandNode != null) { return grandNode; } recycel(childNode); } } return null; } /** * 查找到含应用字段的Node列表后,根据相应字段作排除,只适应节点字段相等的匹配情况。 */ public static AccessibilityNodeInfo findNodeInfosByText(AccessibilityNodeInfo root, String text) { if (root == null) { root = MyAccessibilityService.getInstance().getRootInActiveWindow(); if (root == null) { return null; } } List<AccessibilityNodeInfo> list = root.findAccessibilityNodeInfosByText(text); if (list == null || list.isEmpty()) { return null; } AccessibilityNodeInfo resultNode = null; String nodeText; String nodeDesc; for (AccessibilityNodeInfo node : list) { nodeText = ObjectUtils.objStr(node.getText()); nodeDesc = ObjectUtils.objStr(node.getContentDescription()); if (nodeText == null && nodeDesc == null) { continue; } if (nodeText != null && (nodeText.equals("[" + text + "]") || nodeText.equals(text))) { resultNode = node; break; } if (nodeDesc != null && nodeDesc.equals(text)) { resultNode = node; break; } } for (AccessibilityNodeInfo node : list) { if (!node.equals(resultNode)) { recycel(node); } } return resultNode; } /** * find the parent node that can be clicked */ public static AccessibilityNodeInfo findNodeByParentClick(AccessibilityNodeInfo root, String text) { AccessibilityNodeInfo node = findNodeInfosByText(root, text); if (node == null) { return null; } AccessibilityNodeInfo parent = node; while (parent != null && !parent.isClickable()) { parent = parent.getParent(); } if (parent != null && parent.isClickable()) { return parent; } return null; } private static void recycel(AccessibilityNodeInfo node) { try { if (node != null) { node.recycle(); } } catch (IllegalStateException e) { Log.e("e", e.getMessage()); } } }
其中有个方法为 findNodeByParentClick() 这个方法目的在于找到能够点击的父节点。
在我们查找节点的时候并不是每个节点都是能够点击的,因为它的 clickable 状态为 false,通过 while循环 一直查找到 clickable 状态为 true 的父节点,然后设置点击事件。
如下图,我们需要点击的 发现 按钮,但是这个节点的 clickable 状态为 false ,这就说明它是不能够点击的,调用 info.performAction(AccessibilityNodeInfo.ACTION_CLICK); 也不会起作用,这就需要我们查找到它的能够点击的父节点。
向上一直查找节点的父节点我们就可以看到 RelativeLayout 是可以点击的,就用这个能够点击的 父节点设置 info.performAction(AccessibilityNodeInfo.ACTION_CLICK)。
其中 BaseContact.java 主要用来放节点相关的东西:
/** * the message of node */ public class BaseContact { public static String clsName_Main = "com.tencent.mm.ui.LauncherUI"; public static String clsName_listView = "android.widget.ListView"; public static final String text_find = "发现"; public static final String text_friend_circle = "朋友圈"; }
通过以上步骤就可以简单实现一步打开微信朋友圈功能。
过程中我们需要注意的是,我们需要的节点并不是都可以点击的,另外就是 event.getEventType() 的事件不同,在页面转换后,event.getEventType()可能会有多个值,可以选取唯一一个不同其他的值。
- 点击微信网页的a标签直接跳转到淘宝APP打开怎么实现的?附:动图演示效果
- 微信跳转手机默认浏览器打开指定HTML链接 微信点击链接直接下载安装包实现方式及源码
- 微信中点击链接直接跳到默认浏览器是怎么实现的?(不是在微信内置浏览器打开)
- 微信中点击链接直接跳到默认浏览器是怎么实现的?(不是在微信内置浏览器打开)
- 微信内点击链接或扫描二维码,通过ccjump实现直接打开手机默认浏览器打开指定网页
- 扫描 4000 二维码如何实现从微信内直接跳转外部浏览器打开指定页面
- 微信中直接打开手机系统浏览器的实现
- 微信点击链接直接下载安装包功能实现方式
- 微信内点击链接或扫描二维码可直接用外部浏览器打开H5链接的解决方案
- 微信点击链接跳转外部浏览器打开指定页面的实现
- 微信内直接下载app和提示前往手机默认浏览器打开的实现方案
- 微信超链接自动跳转外部浏览器打开app下载文件,实现微信直接下载APK的解决方法
- java 利用Jframe实现查找鼠标点击的位置坐标
- 微信内点击链接或扫描二维码,直接打开手机默认浏览器打开指定网页
- Android开发——Accessibility机制实现模拟点击(微信自动抢红包实现)
- 微信点击链接跳转外部浏览器打开指定页面的实现
- 安卓手机微信不能打开App下载地址的问题 微信实现点开网址直接跳转浏览器
- [置顶] 利用高德地图关键字检索POI-实现仿微信发朋友圈搜索附近位置
- 利用Go2Shell 实现 Mac Finder 直接shell端打开当前文件夹
- 模拟UA实现访问只能在微信上打开的网页