我的编程空间,编程开发者的网络收藏夹
学习永远不晚

android AccessibilityService无障碍功能开发,实现自动化测试

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

android AccessibilityService无障碍功能开发,实现自动化测试

android AccessibilityService无障碍功能开发,实现自动化测试,这里使用抖音为例子,仅供技术研究学习使用。

使用方法

安装好APP后,需要打开无障碍功能,打开后,在次打开抖音APP,随便找一个直播间,上下滑动切换直接后,实现模拟点击屏幕,可以自动完成关注。

代码如下

package com.nyw.testclick;import androidx.annotation.RequiresApi;import androidx.appcompat.app.AppCompatActivity;import android.accessibilityservice.AccessibilityService;import android.accessibilityservice.AccessibilityServiceInfo;import android.accessibilityservice.GestureDescription;import android.content.Context;import android.content.Intent;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.content.pm.ServiceInfo;import android.graphics.Path;import android.graphics.Point;import android.graphics.Rect;import android.net.Uri;import android.os.Build;import android.os.Bundle;import android.provider.Settings;import android.text.TextUtils;import android.util.Log;import android.view.accessibility.AccessibilityManager;import android.view.accessibility.AccessibilityNodeInfo;import android.widget.Toast;import java.util.List;public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        boolean enabled = isAccessibilityServiceEnabled(this, MyAccessibilityService.class);        //判断是否安装抖音        boolean exist1 = checkAppInstalled(MainActivity.this, "com.ss.android.ugc.aweme");        //抖音极速版        //boolean exist1 = checkAppInstalled(getContext(), "com.ss.android.article.video");        //抖音火山版        //boolean exist1 = checkAppInstalled(getContext(), "com.ss.android.ugc.live");        if (exist1) {            Intent intent = new Intent();            //抖音 打开个人中心  104248958804 需要去获取抖音的UserId//            intent.setData(Uri.parse("snssdk1128://user/profile/104248958804"));            // 打开首页            intent.setData(Uri.parse("snssdk1128://feed?refer=web&gd_label=1"));            //抖音极速版            //intent.setData(Uri.parse("snssdk1112://user/profile/104248958804"));            //抖音火山版            //intent.setData(Uri.parse("snssdk1112://profile?id=104248958804"));            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            startActivity(intent);        } else {            Toast.makeText(MainActivity.this, "请先安装此应用", Toast.LENGTH_SHORT).show();        }        if (enabled==false){            final String actionOpen=Settings.ACTION_ACCESSIBILITY_SETTINGS;//系统辅助功能服务            Intent intent=new Intent(actionOpen);            startActivity(intent);        }    }        public static boolean isAccessibilityServiceEnabled(Context context, Class service) {        AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);        List enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);        for (AccessibilityServiceInfo enabledService : enabledServices) {            ServiceInfo enabledServiceInfo = enabledService.getResolveInfo().serviceInfo;            if (enabledServiceInfo.packageName.equals(context.getPackageName()) && enabledServiceInfo.name.equals(service.getName()))                return true;        }        return false;    }        private boolean checkAppInstalled(Context context, String pName) {        if (pName == null || pName.isEmpty()) {            return false;        }        final PackageManager packageManager = context.getPackageManager();        List info = packageManager.getInstalledPackages(0);        if (info == null || info.isEmpty()) {            return false;        }        for (int i = 0; i < info.size(); i++) {            if (pName.equals(info.get(i).packageName)) {                return true;            }        }        return false;    }}

 自定义一个服务MyAccessibilityService,继承AccessibilityService,实现2个方法,重写一个方法,代码如下

package com.nyw.testclick;import android.accessibilityservice.AccessibilityService;import android.accessibilityservice.AccessibilityServiceInfo;import android.accessibilityservice.GestureDescription;import android.content.Context;import android.graphics.Path;import android.graphics.Point;import android.graphics.Rect;import android.os.Build;import android.provider.Settings;import android.text.TextUtils;import android.util.Log;import android.view.KeyEvent;import android.view.accessibility.AccessibilityEvent;import android.view.accessibility.AccessibilityManager;import android.view.accessibility.AccessibilityNodeInfo;import androidx.annotation.RequiresApi;import java.util.List;public class MyAccessibilityService extends AccessibilityService {    @Override    public void onAccessibilityEvent(AccessibilityEvent event) {        //这个函数可以接收系统发送来的AccessibilityEvent,接收来的AccessibilityEvent是经过过滤的,过滤是在配置工作时设置的        //event.getSource()  这是当前event的节点信息       // AccessibilityService.getRootInActiveWindow();  获取到当前活跃中本服务的可检索到窗口的根节点       // AccessibilityNodeInfo.recycle()//为避免创建重复的实例通过recycle方法回收掉nodeInfo        //event.TYPE_NOTIFICATION_STATE_CHANGED  基本窗口view的变化都可以使用这个type来监听        //event.TYPE_WINDOW_STATE_CHANGED  打开popupwindow,菜单,对话框时候会触发        //event.TYPE_WINDOW_CONTENT_CHANGED  更加精确的代表了基于当前event.source中的子view的内容变化        //event.TYPE_WINDOWS_CHANGED  窗口的变化        //获取到当前活跃中本服务的可检索到窗口的根节点 两种方式获取的childNode个数不一致//        AccessibilityNodeInfo mNodeInfo1 = getRootInActiveWindow();//获取NodeInfo//        AccessibilityNodeInfo mNodeInfo2= event.getSource();//获取NodeInfo        //查找我们需要做操作的view//        List listNodes1 =mNodeInfo1. findAccessibilityNodeInfosByViewId("id");//操作的view,查找我们需要操作的对象方法之一//        List listNodes2=mNodeInfo2.findAccessibilityNodeInfosByText("id");//操作的view,查找我们需要操作的对象方法之一       // findFocus(0);//查找拥有特定焦点类型的控件       // getRootInActiveWindow();//如果配置能够获取窗口内容,则会返回当前活动窗口的根结点//        getServiceInfo();//获取当前服务的配置信息//        performGlobalAction(0);//执行全局操作,比如返回,回到主页,打开最近等操作//        event.getClassName();//获取事件源对应类的类型,比如点击事件是有某个Button产生的,那么此时获取的就是Button的完整类名//        event. getText();//获取事件源的文本信息,比如事件是有TextView发出的,此时获取的就是TextView的text属性.如果该事件源是树结构,那么此时获取的是这个树上所有具有text属性的值的集合//        event.isEnabled();//事件源(对应的界面控件)是否处在可用状态//        event.getItemCount();//如果事件源是树结构,将返回该树根节点下子节点的数量        try {            int eventType = event.getEventType();//事件类型            String packageName = event.getPackageName().toString();            String className = event.getClassName().toString();            List list5 = null;            List list6 = null;            List list7 = null;            AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();            if ("android.widget.ImageView".equals(event.getClassName().toString())) {                perforGlobalClick("com.ss.android.ugc.aweme:id/gsb");                Log.i("sdfkslfsfks", "用户名:" + getTextById("com.ss.android.ugc.aweme:id/m39"));                Log.i("sdfkslfsfks", "文本信息:" + getTextById(" id:com.ss.android.ugc.aweme:id/ag7"));            }            switch (eventType) {                case AccessibilityEvent.TYPE_VIEW_CLICKED:                    // 界面点击                    Log.i("sdfkslfsfks", "页面点击了");                    Log.d("sdfkslfsfks", event.getClassName() + "                    " + event.getSource().getViewIdResourceName());//                logViewHierarchy(getRootInActiveWindow(), 0);//                perforGlobalClick("com.ss.android.ugc.aweme:id/efr");                    break;                case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:                    // 界面文字改动                    Log.i("sdfkslfsfks", "界面文字改动");                    perforGlobalClick("com.ss.android.ugc.aweme:id/gsb");                    break;                case AccessibilityEvent.TYPE_VIEW_SCROLLED:                    //滚动视图的事件。此事件类型通常不直接发送                    Log.e("sdfkslfsfks", "onServiceConnected:" + "实现辅助功能");                    Log.d("TAG", "packageName = " + packageName + ", className = " + className);                    //滑动就自动点赞及关注                    perforGlobalClick("com.ss.android.ugc.aweme:id/gsb");                    if (className.equals("com.lynx.tasm.behavior.KeyboardMonitor")) {                        Log.e("sdfkslfsfks", "执行了搜索按钮");                        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {list5 = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.ss.android.ugc.aweme:id/rzy");                        }                        if (null != list5) {for (AccessibilityNodeInfo info : list5) {    clickByNode(this, info);}                        }                    }                    if (className.equals("androidx.recyclerview.widget.RecyclerView")) {                        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {list6 = rootNodeInfo.findAccessibilityNodeInfosByText("用户");                        }                        if (null != list6) {for (AccessibilityNodeInfo info : list6) {    Log.e("sdfkslfsfks", info.toString());    clickByNode(this, info.getParent());}                        }                        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {list7 = rootNodeInfo.findAccessibilityNodeInfosByText("关注");                        }                        if (null != list7) {for (AccessibilityNodeInfo info : list7) {    Log.e("sdfkslfsfks", info.toString());}                        }                    }                    //获取根节点                    AccessibilityNodeInfo rootNode = getRootInActiveWindow();                    //匹配Text获取节点                    List list1 = rootNode.findAccessibilityNodeInfosByText("match_text");                    //匹配id获取节点                    List list2 = rootNode.findAccessibilityNodeInfosByViewId("match_id");                    //获取子节点                    AccessibilityNodeInfo infoNode = rootNode.getChild(0);                    break;            }        }catch (Exception exception){}    }    @Override    public void onInterrupt() {        //在系统将要关闭这个AccessibilityService会被调用。在这个方法中进行一些释放资源的工作。       // disableSelf();//禁用服务。调用此方法后,服务将被禁用,设置将显示它已关闭。    }    @Override    protected void onServiceConnected() {        super.onServiceConnected();        //重载的方法        //系统成功绑定该服务时被触发调用这个方法,在这个方法里你可以做一下初始化工作,例如设备的声音震动管理,也可以调用setServiceInfo()进行配置工作        //配置,可在这里配制,也可以在values中添加配制文件进行配制,2种方法二选一//        AccessibilityServiceInfo accessibilityServiceInfo=new AccessibilityServiceInfo();//        accessibilityServiceInfo.eventTypes=AccessibilityEvent.TYPES_ALL_MASK;//        accessibilityServiceInfo.feedbackType=AccessibilityServiceInfo.FEEDBACK_GENERIC;//        accessibilityServiceInfo.flags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;//获取View的Id//        accessibilityServiceInfo.notificationTimeout=100;//        accessibilityServiceInfo.packageNames=new String[]{"com.nyw.testclick","com.ss.android.ugc.aweme"};//自动点击的包名//        setServiceInfo(accessibilityServiceInfo);//设置当前服务的配置信息        }//    @Override//    protected boolean onKeyEvent(KeyEvent event) {//        //如果允许服务监听按键操作,该方法是按键事件的回调,需要注意,这个过程发生了系统处理按键事件之前//        return super.onKeyEvent(event);//    }        private boolean enabled(String name) {        AccessibilityManager am = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);        List serviceInfos = am                .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);        List installedAccessibilityServiceList = am                .getInstalledAccessibilityServiceList();        for (AccessibilityServiceInfo info : installedAccessibilityServiceList) {            Log.d("MainActivity", "all -->" + info.getId());            if (name.equals(info.getId())) {                return true;            }        }        return false;    }        private boolean checkStealFeature1(String service) {        int ok = 0;        try {            ok = Settings.Secure.getInt(getApplicationContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);        } catch (Settings.SettingNotFoundException e) {        }        TextUtils.SimpleStringSplitter ms = new TextUtils.SimpleStringSplitter(':');        if (ok == 1) {            String settingValue = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);            if (settingValue != null) {                ms.setString(settingValue);                while (ms.hasNext()) {                    String accessibilityService = ms.next();                    if (accessibilityService.equalsIgnoreCase(service)) {                        return true;                    }                }            }        }        return  false;    }        private boolean viewByText(String str) {        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();        if (nodeInfo != null) {            List list = nodeInfo.findAccessibilityNodeInfosByText(str);            nodeInfo.recycle();            for (AccessibilityNodeInfo item : list) {                if (str.equals(item.getText().toString())) {                    return true;                }            }        }        return false;    }        private String getTextById(String id) {        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();        if (nodeInfo != null) {            List list = nodeInfo.findAccessibilityNodeInfosByViewId(id);            nodeInfo.recycle();            for (AccessibilityNodeInfo item : list) {                return item.getText() + "";            }        }        return "";    }        public void perforGlobalClick(String id) {        try {            AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();            if (nodeInfo != null) {                List list = nodeInfo.findAccessibilityNodeInfosByViewId(id);                nodeInfo.recycle();                for (AccessibilityNodeInfo item : list) {                    item.performAction(AccessibilityNodeInfo.ACTION_CLICK);                }            }        }catch (Exception exception){}    }        public static void logViewHierarchy(AccessibilityNodeInfo nodeInfo, final int depth) {        if (nodeInfo == null) return;        String spacerString = "";        for (int i = 0; i < depth; ++i) {            spacerString += '-';        }        //Log the info you care about here... I choce classname and view resource name, because they are simple, but interesting.        Log.d("sdfkslfsfks", spacerString + nodeInfo.getClassName() + "                    " + nodeInfo.getViewIdResourceName());        for (int i = 0; i < nodeInfo.getChildCount(); ++i) {            logViewHierarchy(nodeInfo.getChild(i), depth+1);        }    }        public static boolean clickByNode(AccessibilityService service, AccessibilityNodeInfo nodeInfo) {        if (service == null || nodeInfo == null) {            return false;        }        Rect rect = new Rect();        nodeInfo.getBoundsInScreen(rect);        int x = rect.centerX();        int y = rect.centerY();        Log.e("acc_", "要点击的像素点在手机屏幕位置::" + rect.centerX() + " " + rect.centerY());        Point point = new Point(x, y);        GestureDescription.Builder builder = new GestureDescription.Builder();        Path path = new Path();        path.moveTo(point.x, point.y);        builder.addStroke(new GestureDescription.StrokeDescription(path, 0L, 100L));        GestureDescription gesture = builder.build();        boolean isDispatched = service.dispatchGesture(gesture, new AccessibilityService.GestureResultCallback() {            @Override            public void onCompleted(GestureDescription gestureDescription) {                super.onCompleted(gestureDescription);//                LogUtil.d(TAG, "dispatchGesture onCompleted: 完成...");            }            @Override            public void onCancelled(GestureDescription gestureDescription) {                super.onCancelled(gestureDescription);//                LogUtil.d(TAG, "dispatchGesture onCancelled: 取消...");            }        }, null);        return isDispatched;    }}

AndroidManifest.xml文件配制如下

                                                                                                                                                                

 在xml中添加一个accessible_service_config文件,代码如下

 

来源地址:https://blog.csdn.net/u013519290/article/details/129395067

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

android AccessibilityService无障碍功能开发,实现自动化测试

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何使用MongoDB实现数据的自动化测试功能

如何使用MongoDB实现数据的自动化测试功能摘要:随着软件开发的不断发展,自动化测试已经成为了一项非常重要的工作。对于使用MongoDB作为后台数据库的项目来说,如何实现数据的自动化测试功能尤为重要。本文将介绍如何使用MongoDB来实现
2023-10-22

通过Go语言开发实现自动化测试的工具与框架

通过Go语言开发实现自动化测试的工具与框架一、绪论随着软件开发的不断进步,自动化测试在保证软件质量和提高开发效率方面扮演着重要角色。而Go语言,作为一种简洁、高效、并发的编程语言,逐渐受到开发者们的喜爱。本文将介绍如何使用Go语言开发实现自
通过Go语言开发实现自动化测试的工具与框架
2023-11-20

Android开发中如何实现一个应用程序开机自启动功能

今天就跟大家聊聊有关Android开发中如何实现一个应用程序开机自启动功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Android在开机时自动启动一个应用程序在启动时自动启动一个
2023-05-31

Android开发之多线程中实现利用自定义控件绘制小球并完成小球自动下落功能实例

本文实例讲述了Android开发之多线程中实现利用自定义控件绘制小球并完成小球自动下落功能的方法。分享给大家供大家参考,具体如下: 1、布局界面2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录