zoukankan      html  css  js  c++  java
  • 如何编写基于Android的AccessibilityService的钉钉自动打卡

    第一节 缘由与准备 

    最近有时间空闲,闲来无事,想到使用钉钉打卡有时会迟到,所以周末的时候去看了相关网上资料,做了个demo。

    材料:定时器,AccessibilityService

    加工方案:使用定时器在签到签退期间内自启,通过AccessibilityService模拟点击:分为签到与签退两种情况。

    签到正常流程:工作-》考勤打卡-》(判断是否弹出窗口-是:我知道了否跳过)-》签到。

    签到迟到流程:工作-》考勤打卡-》迟到打卡。

    签退正常流程:工作-》考勤打卡-》签退。

    工艺难点:签到页中嵌套的是基于WebView的页面,一开始以为无法获取节点,想到通过屏幕中的位置去点击那块区域,查看官方文档发现有个方法getAccessibilityNodeProvider(),得到虚拟节点进行模拟点击。

    第二节:热火朝天

    技能点:判断应用状态,启动指定应用,自定义AccessibilityService控制模拟点击流程

    判断应用状态:

     public static boolean isBackground(Context context) {
            ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
            for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
                if (appProcess.processName.equals(context.getPackageName())) {
                    if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
                        Log.i("后台", appProcess.processName);
                        return true;
                    }else{
                        Log.i("前台", appProcess.processName);
                        return false;
                    }
                }
            }
            return false;
        }
    

      

    启动指定应用:

     public static void doStartApplicationWithPackageName(Context context, String packagename) {
    
            // 通过包名获取此APP详细信息,包括Activities、services、versioncode、name等等
            PackageInfo packageinfo = null;
            try {
                packageinfo = context.getPackageManager().getPackageInfo(packagename, 0);
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            if (packageinfo == null) {
                return;
            }
    
            // 创建一个类别为CATEGORY_LAUNCHER的该包名的Intent
            Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);
            resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);
            resolveIntent.setPackage(packageinfo.packageName);
    
            // 通过getPackageManager()的queryIntentActivities方法遍历
            List<ResolveInfo> resolveinfoList = context.getPackageManager()
                    .queryIntentActivities(resolveIntent, 0);
    
            ResolveInfo resolveinfo = resolveinfoList.iterator().next();
            if (resolveinfo != null) {
                // packagename = 参数packname
                String packageName = resolveinfo.activityInfo.packageName;
                // 这个就是我们要找的该APP的LAUNCHER的Activity[组织形式:packagename.mainActivityname]
                String className = resolveinfo.activityInfo.name;
                // LAUNCHER Intent
                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_LAUNCHER);
    
                // 设置ComponentName参数1:packagename参数2:MainActivity路径
                ComponentName cn = new ComponentName(packageName, className);
    
                intent.setComponent(cn);
                context.startActivity(intent);
            }
        }

    自定义AccessibilityService控制模拟点击流程:

    1.获取点击控件对象(可以通过文本不推荐通过资源id方式点击)

    工作布局的资源ID:

    考勤打卡布局的资源ID(这个id是动态生成的8个都是):

    考勤打卡布局的资源ID:

    2窗口发生变化处理:

     @Override
        public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
            if(isFinish){
                return;
            }
            AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
            if(nodeInfo == null) {
                Log.d(TAG, "rootWindow为空");
                return ;
            }
            switch (accessibilityEvent.getEventType()){
                //当窗口的状态发生改变时
                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
                   if (index==1) {
                        //主页点击工作
                       clickHome(nodeInfo,accessibilityEvent);
                       Log.d(TAG,"工作");
                    } else  if (index==2) {
                        //工作tab点击考勤打卡
                       clickKaoQing(nodeInfo,accessibilityEvent);
                       Log.d(TAG,"点击考勤打卡");
                    } else  if (index==3) {
                        //开始打卡
                       clickKaoQingBtn(nodeInfo,accessibilityEvent);
                       Log.d(TAG,"打卡");
                    }
                    break;
            }
    
        }
    

    3后续处理:

    发现考勤打卡页面是基于webview的h5页面,因此暂时没有好的方法,获取webview对象,以及获取虚拟节点。

    不过如果可以获取到窗口下的webview对象,那么是可以获取页面的虚拟节点,进行模拟点击。打卡是没问题的,由于现在极速打卡的功能,打开应用自动签到。

    参考:

    >Android WebView官方文档

    >Android AccessibilityNodeProvider官方文档

    >基于AccessibilityService制作的钉钉自动签到程序

  • 相关阅读:
    【.NET】Web Service
    【Coding】C# 操作文件(一)
    【设计模式】设计模式概述
    TCP/IP协议
    【.NET】SOAP Web Service
    简单读写xml
    利用winform来承载WCF服务
    在panel里面显示一个窗体
    asp.net 角色管理 MSDN帮助路径
    asp.net ajax MSDN帮助
  • 原文地址:https://www.cnblogs.com/lmf-techniques/p/6992020.html
Copyright © 2011-2022 走看看