第一節 緣由與准備
最近有時間空閑,閑來無事,想到使用釘釘打卡有時會遲到,所以周末的時候去看了相關網上資料,做了個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; } }