package com.itrycn.ta; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.GestureDescription; import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.DownloadManager; import android.content.Context; import android.content.Intent; import android.graphics.Path; import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.provider.DocumentsContract; import android.util.DisplayMetrics; import android.util.Log; import android.util.Size; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Toast; import androidx.core.content.FileProvider; import com.itrycn.ta.script.ScriptSystemInfo; import com.itrycn.ta.utils.AccessibilityLog; import java.io.File; import java.io.IOException; import java.security.PublicKey; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Random; /** * Created by popfisher on 2017/7/11. */ @TargetApi(16) public class AccessibilityOperator { private Context mContext; private static AccessibilityOperator mInstance = new AccessibilityOperator(); private AccessibilityEvent mAccessibilityEvent; private AccessibilityService mAccessibilityService; public Handler UIHandler; public boolean IsExit=false; private boolean result_bool=false; public ScriptSystemInfo SystemInfo=ScriptSystemInfo.getInstance(); private AccessibilityOperator() { } public static AccessibilityOperator getInstance() { return mInstance; } public void init(Context context) { mContext = context; ScriptSystemInfo.getInstance().init(context); } public void updateEvent(AccessibilityService service, AccessibilityEvent event) { if (service != null && mAccessibilityService == null) { mAccessibilityService = service; } if (event != null) { mAccessibilityEvent = event; } } public boolean isServiceRunning() { ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); List services = am.getRunningServices(Short.MAX_VALUE); for (ActivityManager.RunningServiceInfo info : services) { if (info.service.getClassName().equals("com.itryn.ta.AccessibilityTaService")) { return true; } } return false; } private AccessibilityNodeInfo getRootNodeInfo() { AccessibilityEvent curEvent = mAccessibilityEvent; AccessibilityNodeInfo nodeInfo = null; if (Build.VERSION.SDK_INT >= 16) { // 建议使用getRootInActiveWindow,这样不依赖当前的事件类型 if (mAccessibilityService != null) { nodeInfo = mAccessibilityService.getRootInActiveWindow(); if(nodeInfo==null) { Toast("无法获取当前界面内容1"); } //AccessibilityLog.printLog("nodeInfo: " + nodeInfo); } else{ Toast("辅助服务开启失败,请重启软件"); } // 下面这个必须依赖当前的AccessibilityEvent // nodeInfo = curEvent.getSource(); } else { nodeInfo = curEvent.getSource(); if(nodeInfo==null) { Toast("无法获取当前界面内容2"); } } return nodeInfo; } /** * 根据Text搜索所有符合条件的节点, 精确搜索方式 */ public List findNodesByText(String text) { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { return FindByText(nodeInfo,text); } return null; } /** * 根据Text搜索所有符合条件的节点, 精确搜索方式 */ public List findNodesByText(String[] text) { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { return FindByText(nodeInfo,text); } return null; } /** * 根据Text搜索所有符合条件的节点, 模糊搜索方式 */ public List findNodesByContainsText(String text) { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { return FindByContainsText(nodeInfo,text); } return null; } /** * 根据Text搜索所有符合条件的节点, 模糊搜索方式 */ public List findNodesByContainsText(String[] text) { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { return FindByContainsText(nodeInfo,text); } return null; } /** * 根据Text搜索所有符合条件的节点, 精确搜索方式 */ public List FindByText(AccessibilityNodeInfo info,String text) { List list=new ArrayList(); if (info.getChildCount() == 0 && info.getText()!=null) { if(info.getText().toString().equals(text)) { list.add(info); } } else { for (int i = 0; i < info.getChildCount(); i++) { if(info.getChild(i)!=null){ list.addAll(FindByText(info.getChild(i),text)); } } } return list; } /** * 根据Text搜索所有符合条件的节点, 精确搜索方式 */ public List FindByText(AccessibilityNodeInfo info,String[] text) { List list=new ArrayList(); if (info.getChildCount() == 0 && info.getText()!=null) { for (int k=0;k FindByContainsText(AccessibilityNodeInfo info,String[] text) { List list=new ArrayList(); if (info.getChildCount() == 0 && info.getText()!=null) { for (int k=0;k FindByContainsText(AccessibilityNodeInfo info,String text) { List list=new ArrayList(); if (info.getChildCount() == 0 && info.getText()!=null) { if(info.getText().toString().contains(text)) { list.add(info); } } else { for (int i = 0; i < info.getChildCount(); i++) { if(info.getChild(i)!=null){ list.addAll(FindByContainsText(info.getChild(i),text)); } } } return list; } /** * 获取当前页面上的所有控件信息 */ public List GetAllNodes() { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { return GetAllNodes(nodeInfo); } return null; } /** * 获取当前页面上的所有控件信息 */ public List GetAllNodes(AccessibilityNodeInfo info) { List list=new ArrayList(); if (info.getChildCount() == 0) { list.add(info); } else { for (int i = 0; i < info.getChildCount(); i++) { list.add(info); if(info.getChild(i)!=null){ list.addAll(GetAllNodes(info.getChild(i))); } } } return list; } /** * 获取当前页面上的所有没有子控件的控件信息 */ public List GetAllNoChildsNodes() { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { return GetAllNoChildsNodes(nodeInfo); } return null; } /** * 获取当前页面上的所有没有子控件的控件信息 */ public List GetAllNoChildsNodes(AccessibilityNodeInfo info) { List list=new ArrayList(); if (info.getChildCount() == 0) { list.add(info); } else { for (int i = 0; i < info.getChildCount(); i++) { if(info.getChild(i)!=null){ list.addAll(GetAllNodes(info.getChild(i))); } } } return list; } /** * 根据View的ID搜索符合条件的节点,精确搜索方式; * 这个只适用于自己写的界面,因为ID可能重复 * api要求18及以上 * @param viewId */ public List findNodesById(String viewId) { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { if (Build.VERSION.SDK_INT >= 18) { return nodeInfo.findAccessibilityNodeInfosByViewId(viewId); } } return null; } /** * 按文本来点击 * @param text * @return 是否点击成功 */ public boolean clickByText(String text) { return performClick(findNodesByText(text)); } /** * 按文本来点击 * @param text * @return 是否点击成功 */ public boolean clickByText(String[] text) { return performClick(findNodesByText(text)); } /** * 按文本来点击,忽略黑名单中的控件 * @param text * @return 是否点击成功 */ public boolean clickByText(String[] text,Rect[] blackrect) { runOnUiThread(new Runnable(){ public void run(){ List list=findNodesByText(text); if(list==null) { //Toast("界面查找出错..."); result_bool=false; } else { for (int i = list.size() - 1; i >= 0; i--) { AccessibilityNodeInfo node = list.get(i); Rect rect = new Rect(); node.getBoundsInScreen(rect); for (int r = 0; r < blackrect.length; r++) { if (isIntersect(rect, blackrect[r])) { list.remove(i); } } } result_bool = performClick(list); } } }); return result_bool; } private boolean isIntersect(Rect a, Rect b) { return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom; } /** * 根据View的ID搜索符合条件的节点,精确搜索方式; * 这个只适用于自己写的界面,因为ID可能重复 * api要求18及以上 * @param viewId * @return 是否点击成功 */ public boolean clickById(String viewId) { return performClick(findNodesById(viewId)); } public boolean performClick(List nodeInfos) { if (nodeInfos != null && !nodeInfos.isEmpty()) { AccessibilityNodeInfo node; for (int i = 0; i < nodeInfos.size(); i++) { node = nodeInfos.get(i); // 获得点击View的类型 AccessibilityLog.printLog("View类型:" + node.getClassName()); // 进行模拟点击 if (node.isEnabled()) { return ClickByNode(node); } } } return false; } /** * 从指定坐标1滑动到指定坐标2 * @return 是否点击成功 */ public boolean Gesture(int x1,int y1,int x2,int y2,long duration){//仿滑动 if(IsExit){return false;} if(mAccessibilityService==null){Toast("辅助服务已失效"); return false;} if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { runOnUiThread(new Runnable(){ public void run(){ Path path = new Path(); path.moveTo(x1, y1);//滑动起点 path.lineTo(x2, y2);//滑动终点 GestureDescription.Builder builder = new GestureDescription.Builder(); GestureDescription description = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, duration)).build(); //100L 第一个是开始的时间,第二个是持续时间 result_bool= mAccessibilityService.dispatchGesture(description, new MyCallBack(), null); } }); return result_bool; } return false; } /** * 从指定坐标1滑动到指定坐标2 * @return 是否点击成功 */ public boolean Gesture(Path path,long startTime,long duration){//仿滑动 if(IsExit){return false;} if(mAccessibilityService==null){return false;} if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { GestureDescription.Builder builder = new GestureDescription.Builder(); GestureDescription description = builder.addStroke(new GestureDescription.StrokeDescription(path, startTime, duration)).build(); //100L 第一个是开始的时间,第二个是持续时间 return mAccessibilityService.dispatchGesture(description, new MyCallBack(), null); } return false; } /** * 获取控件矩形参数 * @return 返回矩形 */ public Rect GetBounds(AccessibilityNodeInfo node){ Rect rect = new Rect(); if(node!=null) { node.getBoundsInScreen(rect); } return rect; } /** * 点击指定控件 * @return 是否点击成功 */ public boolean ClickByNode(AccessibilityNodeInfo node){//点击 if(node!=null) { Rect rect = new Rect(); node.getBoundsInScreen(rect); int x=(rect.left+rect.right)/2; int y = (rect.top + rect.bottom) / 2; //设置随机位移,防止作弊被发现 int width_d=RandomInt(0,((int)rect.width()/2)-2); int height_d=RandomInt(0,((int)rect.height()/2)-2); int add=RandomInt(0,1); if(add==0) { x=x-width_d; y=y-height_d; } else{ x=x+width_d; y=y+height_d; } return Click(x,y); } return false; } /** * 生成随机数 * @return */ public int RandomInt(int min,int max) { return (new Random()).nextInt((max - min) + 1) + min; } /** * 生成随机数 * @return */ public long RandomLong(long min,long max) { Random random = new Random(); return ( long )random.nextDouble()*(max-min) + min; } /** * 点击指定坐标 * @return 是否点击成功 */ public boolean Click(int x,int y){//点击 return Press(x,y,150); } /** * 长点击指定坐标 * @return 是否点击成功 */ public boolean LongClick(int x,int y){//点击 return Press(x,y,700); } /** * 点击指定坐标 * @return 是否点击成功 */ public boolean Press(int x,int y,long delay){//点击 return Gesture(x,y,x,y,delay); } /** * 运行app * @return 是否成功 */ public boolean LaunchApp(String packageName) { if(IsExit){return false;} Intent intent =mContext.getPackageManager().getLaunchIntentForPackage(packageName); if (intent != null) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); return true; } return false; } /** * 安装应用 * @param Path */ private void _InstallApk(String Path) { Uri uri; File apkFile; try { apkFile = new File(Path); if(!apkFile.exists()){return;} Intent intentInstall = new Intent(Intent.ACTION_VIEW); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ Uri contentUri = FileProvider.getUriForFile(mContext, "com.rfid.application.MyApplication.provider", apkFile); intentInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intentInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intentInstall.setDataAndType(contentUri, "application/vnd.android.package-archive"); }else{ intentInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intentInstall.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); } mContext.startActivity(intentInstall); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 休眠多少毫秒 */ public void Sleep(long millis) { Date date=new Date(); long addtime= date.getTime()+millis; while(addtime>=(new Date()).getTime() && !IsExit) { try { Thread.sleep(10); } catch (Exception e){e.printStackTrace();} } } /** * 休眠多少毫秒 */ public void Sleep(long millis_min,long millis_max) { long R_millis=RandomLong(millis_min,millis_max); Sleep(R_millis); } /** *Toast提示 */ public void Toast(String text) { runOnUiThread(new Runnable(){ public void run(){ Toast.makeText(mContext.getApplicationContext(), text, Toast.LENGTH_SHORT).show(); } }); } public final void runOnUiThread(Runnable action) { if(UIHandler==null) { action.run(); }else { UIHandler.post(action); } } /** *点击返回键 */ public boolean clickBackKey() { return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); } private boolean performGlobalAction(int action) { if(IsExit){return false;} if(mAccessibilityService==null){return false;} runOnUiThread(new Runnable(){ public void run(){ result_bool= mAccessibilityService.performGlobalAction(action); } }); return result_bool; } }