Android AccessibilityService学习之分析Auto.js实现自动双指捏合,三指下滑 您所在的位置:网站首页 autojs下滑到底 Android AccessibilityService学习之分析Auto.js实现自动双指捏合,三指下滑

Android AccessibilityService学习之分析Auto.js实现自动双指捏合,三指下滑

2023-08-17 12:19| 来源: 网络整理| 查看: 265

Auto.js是什么?

Github链接:https://github.com/hyb1996/Auto.js

1.由无障碍服务实现的简单易用的自动操作函数。 2.悬浮窗录制和运行更专业&强大的选择器API,提供对屏幕上的控件的寻找、遍历、获取信息、操作等。类似于Google的UI测试框架UiAutomator,您也可以把他当做移动版UI测试框架使用。

以上摘自Github简介,通俗来说它是通过AccessibilityService的服务,结合javascript的脚本来实现类似于按键精灵的一款可编写脚本的安卓辅助软件。

这篇文章的目的?

这篇文章将通过分析Auto.js 与 AccessibilityService相关部分的代码来学习AccessibilityService的进阶知识,通过这篇文章的学习,我们来更好的掌握AccessibilityService的主动操作知识,比如实现手势轨迹,模拟下拉通知栏(非调用系统API),双指捏合,三指下滑等。

First Step

了解AccessibilityService,我们可以通过其他博主的文章来了解,不过我更推荐使用官方文档来看,附上链接:https://developer.android.com/reference/android/accessibilityservice/AccessibilityService 也可以学习我的微信自动回复的文章来了解,去博客里找一下不附链接了。 通过学习需要掌握关于AccessibilityService的基本使用,可以自己写一个Demo安装在自己的手机中,在onServiceConnected()方法中实现点击开启服务后弹出一个Toast,这样就算走完的第一步。

Second Step

做完了第一步,接下来我们再回到官方文档,来看看几个重要的方法和类。 Public methods

返回值方法名(描述)final booleandispatchGesture(GestureDescription gesture, AccessibilityService.GestureResultCallback callback, Handler handler)Dispatch a gesture to the touch screen.解释上面这个dispatchGesture方法是把手势分发,手势可以模拟用户全部操作,如手指点击、滑动。进行中的手势只允许有一个,多次发送手势,之前正在进行的手势会被取消。留下一个问题,这个可以实现三指下滑吗?final booleanperformGlobalAction(int action)Performs a global action.解释发送点击,发送文本改变,发送获取焦点,发送home、back,复制粘贴等操作,具体的可以AccessibilityNodeInfo类中查找。

了解了以上知识,我们已经可以开始对Auto.js下手了。下载源码后我们找到GlobalActionAutomator.class,这里Auto.js的作者通过AccessibilityService实现了类似按键精灵的操作,用户在不用root的情况下也可以使用模拟点击,我们来看看代码。

学习dispatchGesture //这句话要求api版本是N(7.0)以上 @RequiresApi(api = Build.VERSION_CODES.N) //函数gesture传入了三个参数分别是延时,执行时间和一个int的二维数组。= public boolean gesture(long start, long duration, int[]... points) { //这里通过下面的pointsToPath方法将points数组转换为路径。 Path path = pointsToPath(points); 这里调用gestures函数,这个函数等下来看 return gestures(new GestureDescription.StrokeDescription(path, start, duration)); } //解析函数传入一个二维数组,事实上是一个int[n][2]的函数。 private Path pointsToPath(int[][] points) { Path path = new Path(); //moveTo(float x, float y) Set the beginning of the next contour to the point (x,y). //这个方法是将触点移动到下个动作的开始位置 path.moveTo(scaleX(points[0][0]), scaleY(points[0][1])); //lineTo(float x, float y)Add a line from the last point to the specified point (x,y). //这个方法是从上个点到这个点直接划线,相当于在屏幕上划一条直线。 //通过循环将各个点之间连起来 for (int i = 1; i //这里的mService其实就是我们的AccessibilityService if (mService == null) return false; //使用GestureDescription.Builder().addStroke()方法添加GestureDescription.StrokeDescription类。 //这里addStroke()方法是可以多次调用的,可以加入不同的路径同时模拟。 //这样就可以通过这个方法传入多个path来实现双指捏合,三指下滑。 GestureDescription.Builder builder = new GestureDescription.Builder(); for (GestureDescription.StrokeDescription stroke : strokes) { builder.addStroke(stroke); } //下面都是不同的构造方法,自己看看就行了。 if (mHandler == null) { return gesturesWithoutHandler(builder.build()); } else { return gesturesWithHandler(builder.build()); } }

这个类其实就分析完了,很简单,Auto.js的作者通过一个int二维数组转路径的方法就实现了路径的转存工作。下面附上一个路径执行的代码转换,可以更好的理解。

accessibilityService .dispatchGesture(new GestureDescription.Builder() .addStroke(new GestureDescription. StrokeDescription(path, 0, 500)).build() , 回调函数(可以为null), 回调的线程(null表示主线程));

上面代码等价于下面的代码,我是从Auto.js作者的角度进行分解的。

GestureDescription.Builder builder = new GestureDescription.Builder(); Path path = new Path(); GestureDescription.StrokeDescription stroke = new GestureDescription.StrokeDescription(path,0,500); builder.addStroke(stroke); accessibilityService.dispatchGesture(builder.build(),null,null); 学习performGlobalAction

这个部分的相关内容在SimpleActionAutomator.java文件内,我们可以通过学习这个类来学习performGlobalAction,话不多说看代码。

public boolean back() { return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); }

先来个简单的,这个方法很简单,就是直接调用了系统给返回方法,使用performGlobalAction,就可以模拟点击返回按钮。当然了,系统提供了很多方法,比如点击Home键,下拉通知栏等等,这里大家可以去看官方文档,就不多说了。

public boolean click(ActionTarget target) { return performAction(target.createAction(AccessibilityNodeInfo.ACTION_CLICK)); }

看看这段代码,好像很简单啊,其实不然,Auto.js作者在这里把控件的方法执行进行的分装,我们要一步一步去找,那就开始找吧。我们的着手点事target,也就是ActionTarget类。打开之后可以看见其实是个接口,里面封装了几个内部类。随便找一个来看看。

class TextActionTarget implements ActionTarget { String mText; int mIndex; public TextActionTarget(String text, int i) { mText = text; mIndex = i; } @Override public SimpleAction createAction(int action, Object... params) { return ActionFactory.createActionWithTextFilter(action, mText, mIndex); } }

这是接口ActionTarget的一个内部类,它实现了ActionTarget方法,我们看createAction方法,事实上它又调用了ActionFactory的createActionWithTextFilter方法,那接着找呗。

public static SimpleAction createActionWithTextFilter(int action, String text, int index) { if (searchUpAction.containsKey(action)) return new SearchUpTargetAction(action, new FilterAction.TextFilter(text, index)); else return new DepthFirstSearchTargetAction(action, new FilterAction.TextFilter(text, index)); }

找到了ActionFactory类里的createActionWithTextFilter方法,它判断了我们要执行的Action来判断新建哪个类,下面是执行new一个SearchUpTargetAction类时Action的类型列表

private static Map searchUpAction = new MapEntries() .entry(AccessibilityNodeInfo.ACTION_CLICK, null) .entry(AccessibilityNodeInfo.ACTION_LONG_CLICK, null) .entry(AccessibilityNodeInfo.ACTION_SELECT, null) .entry(AccessibilityNodeInfo.ACTION_FOCUS, null) .entry(AccessibilityNodeInfo.ACTION_SELECT, null) .entry(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null) .entry(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null) .map(); Ⅰ 、路线SearchUpTargetAction

当执行这些Action时会new SearchUpTargetAction(),我们去找这个类,看看它是如何做的。

@Override public boolean perform(List nodes) { boolean performed = false; for (UiObject node : nodes) { node = searchTarget(node); if (node != null && performAction(node)) { performed = true; } } return performed; } protected boolean performAction(UiObject node) { return node.performAction(mAction); } public int getAction() { return mAction; } public UiObject searchTarget(UiObject node) { return node; }

这个类里的关键方法就是perform()它又调用了performAction()方法,其实到这里思路已经基本清楚了,performAction()方法相当于执行了我们target组件的相关方法,包括点击,长点击等操作。

Ⅱ 路线DepthFirstSearchTargetAction public UiObject searchTarget(UiObject n) { if (n == null) return null; if (mAble.isAble(n)) return n; for (int i = 0; i private Button button, button1; private AccessibilityService accessibilityService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initAutoAcService(); accessibilityService = AutoAcService.getAutoACService(); Log.d("accessibilityService", accessibilityService.toString()); button = (Button) findViewById(R.id.button); button1 = (Button) findViewById(R.id.button2); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { pullDownNotification(); } }); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { viewClick(); } }); } private void initAutoAcService() { if (!AutoAcUtil.isServiceON(this, AutoAcService.class.getName())) { Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } } private void pullDownNotification() { Path path = new Path(); Path path1 = new Path(); Path path2 = new Path(); path.moveTo(300, 20); path.lineTo(300, 1500); for (int i = 0; i accessibilityService.dispatchGesture(new GestureDescription.Builder().addStroke(new GestureDescription.StrokeDescription(path, 0 , 500)).build(), null, null); } } } private void viewClick() { AccessibilityNodeInfo nodeInfo = AutoAcService.getNodeInfo(); if (nodeInfo == null) { AutoAcUtil.toastShow(this,"nodeInfo is null",1); return; } AccessibilityNodeInfo target = findNodeInfosByText(nodeInfo, "BUTTON"); target.performAction(AccessibilityNodeInfo.ACTION_CLICK); } public AccessibilityNodeInfo findNodeInfosByText(AccessibilityNodeInfo nodeInfo, String text) { List list = nodeInfo.findAccessibilityNodeInfosByText(text); if (list == null || list.isEmpty()) { return null; } return list.get(0); } }

有两个按钮第一个实现了dispatchGesture方法来实现一个简单的下拉通知栏的操作。第二个按钮这是实现了performAction方法,通过查找找到第一个按钮进行点击,也会下拉通知栏。附上剩下的几个类。 AutoAcService类:

public class AutoAcService extends AccessibilityService { private static AutoAcService autoACService; public static AccessibilityNodeInfo getNodeInfo() { return nodeInfo; } private static AccessibilityNodeInfo nodeInfo; public AutoAcService() { super(); autoACService = this; } public static AutoAcService getAutoACService(){ if (autoACService != null){ return autoACService; } return null; } @Override public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { nodeInfo = getRootInActiveWindow(); } @Override public void onInterrupt() { } }

AutoAcUtil类:

public class AutoAcUtil { public static boolean isServiceON(Context context,String className) { ActivityManager activityManager = (ActivityManager) context.getSystemService(context.ACTIVITY_SERVICE); List runningServices = activityManager.getRunningServices(100); if (runningServices.size() ComponentName service = runningServices.get(i).service; if (service.getClassName().contains(className)) { return true; } } return false; } public static void toastShow(Context context, String msg, int time) { if (time == 0) { Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } else if (time == 1) { Toast.makeText(context, msg, Toast.LENGTH_LONG).show(); } } }

当然了这些类提供的方法你们也可以自己看一下,最后在Manifest里声明一下服务和权限就可以运行了。

Fourth

以上就是通过分析Auto.js来学习AccessibilityService,当然了这里的思路并不一定是Auto.js作者的,但是我们能实现相关方法了解AccessibilityService就可以了。

Demo代码下载:https://download.csdn.net/download/qq_35928566/10828033



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有