Android开发-AccessibilityService的操作姿势

我们来了解下Android的辅助功能AccessibilityService,Android提供这个类的初衷是辅助人们去使用Android设备,但通过我们的了解,才发现它的作用不仅仅只有这样。我们可用它进行自动化抢红包,自动安装的等等。
AccessibilityService运行在后台,并且能够收到由系统发出的一些事件(AccessibilityEvent,这些事件表示用户界面一系列的状态变化),比如焦点改变,输入内容变化,按钮被点击了等等,该种服务能够请求获取当前活动窗口并查找其中的内容。在开发过程中我们主要做的只有:get/is/find等获取数据方法。以及performAction/dispatchGesture操作。
Android辅助功能
现在我们通过一个抢红包的实例来了解AccessibilityService的使用。

  1. 首先我们先了解下这个服务
public class TestService extends AccessibilityService {
  /**
   *  必须重写的方法:此方法用了接受系统发来的event。在你注册的event发生是被调用。在整个生命周期会被调用多次。
   */
  @Override
  public void onAccessibilityEvent(AccessibilityEvent event) {
  }

 /**
   *  必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。
   */
  @Override
  public void onInterrupt() {
  }

   /**
   *  当系统连接上你的服务时被调用
   */
  @Override
  protected void onServiceConnected() {
      super.onServiceConnected();
  }


  /**
   *  在系统要关闭此service时调用。
   */
  @Override
  public boolean onUnbind(Intent intent) {
      return super.onUnbind(intent);
  }
}
  1. 给辅助服务写一个配置文件
<accessibility-service
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:accessibilityEventTypes="typeAllMask"
  android:accessibilityFeedbackType="feedbackGeneric"
  android:accessibilityFlags="flagDefault"
  android:canRetrieveWindowContent="true"
  android:description="@string/accessibility_description"
  android:notificationTimeout="10" />

这些属性所代表的含义

  • accessibilityEventTypes:响应那种类型的事件,比如typeAllMask就是响应全部事件,typeNotificationStateChanged就是响应通知状态的改变,以及还有typeWindowStateChanged|typeWindowContentChanged如果需要响应多种事件类型可以以 ‘ | ’ 隔开。
  • accessibilityFeedbackType:用什么方式反馈给用户
  • notificationTimeout:响应时间
  • packageNames:指定响应哪个应用的事件。如果不填则是响应所有的应用事件(如果以后写抢红包的辅助功能,可以只写微信的包名)
  • description:辅助服务的描述信息。
  1. 在manifest中注册服务
<service
            android:name=".TestService"
            //辅助功能的名称
            android:label="@string/test_service_label"
            //此处必须声明一次权限
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
            //对,我就是上面写好的配置文件
                android:name="android.accessibilityservice"
                android:resource="@xml/text_service_config" />
        </service>
  1. 配置权限
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
  1. 最后就可以在服务类中,写我们的监听方法了
public void onAccessibilityEvent(AccessibilityEvent event) {
    //得到事件的包名。如果注册了多个应用的事件,可以在此做一个判断。
    String packageName = event.getPackageName().toString();
    
    //得到对应的事件类型,这里有很多很多种的事件类型,具体可以自行翻阅AccessibilityEvent类中的定义。
    int eventType = event.getEventType();
    
    //得到根的view节点。可以当做当前acitivity的视图看成是树状结构的(实际上也是~。~),而我们现在就得到了它的根节点。
    AccessibilityNodeInfo root = getRootInActiveWindow();
    
    //我们可以得到此节点的文字
    String rootText = root.getText().toString();
    //得到此节点的class
    String rootClass = root.getClass().toString();
    //得到子节点的和子节点总数
    root.getChild(root.getChildCount()-1);
 }

当找到视图上想要的控件。就可以进行下来一写简单操作

//这两个方法如果找不到的话,都会报错。所以请做好对应的处理。
    root.findAccessibilityNodeInfosByText("根据文本内容查找节点");
    root.findAccessibilityNodeInfosByViewId("根据id查找节点,当然实际上很难知道id是多少~、~");

//点击操作
    root.performAction(AccessibilityNodeInfo.ACTION_CLICK);

    //滑动操作
    root.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);

抢红包插件

分析主要流程;

  1. 状态栏出现"[微信红包]"的消息提示,点击进入聊天界面
  2. 点击相应的红包信息,弹出抢红包界面
  3. 在抢红包界面点击"开",打开红包
  4. 在红包详情页面,查看详情,点击返回按钮返回微信聊天界面.

这里我们只演示状态栏出现消息提示的情况。

具体代码如下

public class HongbaoService extends AccessibilityService{

    private KeyguardManager.KeyguardLock kl;
    /**
     * 是否有打开微信页面
     */
    private boolean isOpenPage = false;

    /**
     * 是否点击了红包
     */
    private boolean isOpenRP = false;

    /**
     * 是否点击了开按钮,打开了详情页面
     */
    private boolean isOpenDetail = false;

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

        PrintUtils.printEvent(event);
        int eventType = event.getEventType();
        switch (eventType) {
            //通知栏
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
                handleNotification(event);
                break;
            //窗体状态发生改变
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
            //页面跳转事件
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:

                if (isOpenPage){
                    isOpenPage = false;

                    String className = event.getClassName().toString();
                    if (className.equals("com.tencent.mm.ui.LauncherUI")) {
                        getPacket();
                    } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {
                       // openPacket();
                        if (!findOpenBtn()){
                            close();
                        }
                    } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
                        close();
                    }
                }
                break;
        }
    }


    @Override
    public void onInterrupt() {

    }

    /**
     * 当系统连接上你的服务时被调用
     */
    @Override
    protected void onServiceConnected() {
        Toast.makeText(this, "连接下啦~~", Toast.LENGTH_SHORT).show();
        super.onServiceConnected();
    }

    /**
     * 在系统要关闭此service时调用。
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Toast.makeText(this, "拜拜~~", Toast.LENGTH_SHORT).show();
        return super.onUnbind(intent);
    }

    //打开通知信息
    private void handleNotification(AccessibilityEvent event){
        List<CharSequence> texts = event.getText();
        if (!texts.isEmpty()){
            for (CharSequence text : texts){
                String content = text.toString();

                if (content.contains("[微信红包]")){

                    if (isScreenLocked()){
                        wakeAndUnlock();
                        openWeichaPage(event);
                    } else {
                        openWeichaPage(event);
                    }

                }
            }
        }

    }

    private void openWeichaPage(AccessibilityEvent event){
        //获取Parcelable对象,用于传递对象
        if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification){
            Notification notification = (Notification) event.getParcelableData();

            //得到通知栏的信息
            String content = notification.tickerText.toString();
            String name = content.substring(0, content.indexOf(":"));
            String scontent = content.substring(content.indexOf(":"), content.length());
            Log.d("mylog", "------openWeichaPage  name: " + name + " content: " + scontent);

            isOpenPage = true;
            PendingIntent pendingIntent = notification.contentIntent;
            try {
                //打开通知
                pendingIntent.send();
            } catch (PendingIntent.CanceledException e) {
                e.printStackTrace();
            }
        }
    }

    //根据查看的id.拆开红包,
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void openPacket() {
        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo != null){
            //根据id获得节点
            List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("@id/cnu");
            nodeInfo.recycle();

            for (AccessibilityNodeInfo item : list){
                item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }

    //根据唯一button控件,来拆开红包,
    private boolean findOpenBtn() {
        AccessibilityNodeInfo rootNode = getRootInActiveWindow();
        for (int i = 0; i < rootNode.getChildCount(); i++) {
            AccessibilityNodeInfo nodeInfo = rootNode.getChild(i);
            Log.d("mylog", "--------RP node className = " + nodeInfo.getClassName() + " cd:" + nodeInfo.getContentDescription());
//            if ("android.widget.TextView".equals(nodeInfo.getClassName()))
//            {
//                Log.d("mylog", "----------RPtextview" + nodeInfo.getText());
//            }
            if ("android.widget.Button".equals(nodeInfo.getClassName())) {
                Log.d("mylog", "----------RPbutton");
                nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                return true;
            }
            findOpenBtn();
        }
        return false;
    }

    //打开红包界面
    public void getPacket() {
        AccessibilityNodeInfo rootNode = getRootInActiveWindow();
        AccessibilityNodeInfo node = recycle(rootNode);


        node.performAction(AccessibilityNodeInfo.ACTION_CLICK);


        AccessibilityNodeInfo parent = node.getParent();
        while (parent != null) {
            if (parent.isClickable()) {
                parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                break;
            }
            parent = parent.getParent();


        }

    }

    //查找聊天中的红包信息
    private AccessibilityNodeInfo recycle(AccessibilityNodeInfo node) {
        if (node.getChildCount() == 0) {
            if (node.getText() != null) {
                if ("领取红包".equals(node.getText().toString())) {
                    return node;
                }
            }
        } else {
            for (int i = 0; i < node.getChildCount(); i++) {
                AccessibilityNodeInfo nodeInfo = node.getChild(i);
                if (nodeInfo != null) {
                    Log.d("mylog", "--------nodeinfo  class = " + nodeInfo.getClassName() + " ds = " + nodeInfo.getContentDescription());
                    recycle(node.getChild(i));
                }
            }

        }
        return node;
    }

    //关闭红包,返回聊天窗口
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void close(){
        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo != null){
            List<AccessibilityNodeInfo> infos = nodeInfo.findAccessibilityNodeInfosByViewId("@id/jb");
            nodeInfo.recycle();

            for (AccessibilityNodeInfo item : infos){
                item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }

    /**
     * 系统是否在锁屏状态
     *
     * @return
     */
    private boolean isScreenLocked() {
        KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
        return keyguardManager.inKeyguardRestrictedInputMode();
    }

    private void wakeAndUnlock() {
        //获取电源管理器对象
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

        //获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag
        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");

        //点亮屏幕
        wl.acquire(1000);

        //得到键盘锁管理器对象
        KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
        kl = km.newKeyguardLock("unLock");

        //解锁
        kl.disableKeyguard();

    }

    /**
     * 回到系统桌面
     */
    private void back2Home() {
        Intent home = new Intent(Intent.ACTION_MAIN);

        home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        home.addCategory(Intent.CATEGORY_HOME);

        startActivity(home);
    }
}

里面我们获取返回键的时候,直接通过findAccessibilityNodeInfosByViewId()获取制定id控件。那怎么可以知道控件的id呢,可以通过在Android Studio中开启Android Device Monitor,这样我们就可以很方便的查看界面id了。

参考:
你真的理解AccessibilityService吗
自动抢红包,自动安装原理之AccessibilityService
遍历list和输入框的使用
https://www.jianshu.com/p/cd1cd53909d7

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值