前言
最初,我有一个朋友问我能不能有办法监听到另一个App界面的内容,一旦有特定的消息出现就提醒用户,就这样,我接触到了AccessibityService。这个项目很好写,所以很快就实现了。主要是那个app结点都能直接获取到,也都能点击。有一天,我收蚂蚁森林能量,好友比较多,就在想,我能不能写一个基于AccessibilityService的自动收集能量的应用,造福一下懒人
运行效果
APP已放在末尾,欢迎大家测试
所用技术
AccessibilityService + 多线程 + 手势
项目开始
实现逻辑
说明
所有判断页面是否加载完成,用户名是否匹配的操作都是在开辟的子线程(图中绿色部分)中进行,主线程的MainHandler 接收来自子线程发送的消息,进行处理。
一些问题的解决
一、怎么获得webView中的结点信息
二、怎么判断webView加载完成了
三、怎么点击按钮
第一个问题,webView就像浏览器一样,里面的内容是网页的内容。
在webView中,所有的通过搜索获取结点List的方法失效了,但是可以通过不停地得到子节点、判断、得到子节点、判断的方法粗略的得到结点信息。
可以借助安卓的 uiautomatorviewer 工具查看结点树,但是这里的结点树也可能和实际运行的有点点出入,主要是webview 加载成功和不成功 之间有些变化,其他的结点结构是固定的,所以可取。
我是用dfs的方式得到webView的结点
private AccessibilityNodeInfo returnWebView(AccessibilityNodeInfo nowNode){
if(nowNode==null) return null;
if(nowNode.getClassName().toString().equals("android.webkit.WebView")){
return nowNode;
}
if(nowNode.getChildCount()==0) return null;
int size = nowNode.getChildCount();
AccessibilityNodeInfo webViewNode = null;
for(int i=0;i<size;i++){
webViewNode = returnWebView(nowNode.getChild(i));
if(webViewNode!=null) return webViewNode;
}
return null;
}
uiautomatorviewer具体怎么使用网上有很多博客,就不说了
第二个问题,怎么判断webView是否加载完成。
老实说,没有特别好的办法。你看一般的浏览器加载网页,有些时候加载的快,有些时候加载得慢,网页上很多东西的加载都是异步的。我很难准确的找到一个判定条件说网页绝对加载完成了。我用的办法是子线程判断关键信息是否加载完成了,只要我要点击的按钮加载完成了,就判定webView加载完成了。
private void LoadingForest(){
//判定已载入的规则:只包含能量的view节点子孩子数大于等于4
new Thread(new Runnable() {
@Override
public void run() {
debug("加载蚂蚁森林...");
Message message = Message.obtain();
int MaxCount = MAX_REQUEST_TIME;
WebViewNode = null;
while(MaxCount > 0 ){
MaxCount--;
sleep(500);
rootNode = getRootInActiveWindow();//Frame
if(rootNode==null || rootNode.getChildCount()==0) continue;
WebViewNode = returnWebView(rootNode);
if(WebViewNode==null || WebViewNode.getChildCount()==0) continue;
nowNode = WebViewNode.getChild(0);
if(nowNode==null || nowNode.getChildCount()==0) continue;
nowNode = nowNode.getChild(0);
if (nowNode==null || nowNode.getChildCount()<3) continue;
nowNode = nowNode.getChild(2);
// dfs(nowNode);
if(nowNode.getChildCount()>=4) break;
}
if(MaxCount==0){
message.what = -1;
}else{
message.what = 6;
}
MainHandler.sendMessage(message);
}
}).start();
}
第三个问题 ,怎么点击
通过手势,可以实现滑动 和点击
滑动
private void scroll(){
new Thread(new Runnable() {
@Override
public void run() {
int sx = (nowUserEntrance.left+nowUserEntrance.right)>>1;
int sy = nowUserEntrance.bottom;
int ey = nowUserEntrance.top-21;
dispatchGestureScroll(2,sx,sy,sx,ey);
sleep(100);
Message message = Message.obtain();
message.what = 10;
MainHandler.sendMessage(message);
}
}).start();
}
private void dispatchGestureScroll(final int flag, int sx, int sy,int ex,int ey) {
// debug("sx:"+sx+"sy:"+sy+"ex:"+ex+"ey:"+ey);
GestureDescription.Builder builder = new GestureDescription.Builder();
Path p = new Path();
p.moveTo(sx, sy);
p.lineTo(ex, ey);
builder.addStroke(new GestureDescription.StrokeDescription(p, 0L, 100L));
GestureDescription gesture = builder.build();
dispatchGesture(gesture, new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
Log.d(TAG, flag+"onCompleted: 完成..........");
}
@Override
public void onCancelled(GestureDescription gestureDescription) {
super.onCancelled(gestureDescription);
Log.d(TAG, flag+"onCompleted: 取消..........");
}
}, null);
}
点击
/**
* 模拟点击事件
* @param flag 点击的控件类型(1 能量) (0 用户入口)
* @param x 横坐标
* @param y 纵坐标
*/
private boolean dispatchGestureView(final int flag, int x, int y) {
boolean res = false;
GestureDescription.Builder builder = new GestureDescription.Builder();
Path p = new Path();
p.moveTo(x, y);
p.lineTo(x, y);
builder.addStroke(new GestureDescription.StrokeDescription(p, 0L, 100L));
GestureDescription gesture = builder.build();
Log.d("","点击了位置"+"("+x+","+y+")");
sleep(200);
res = dispatchGesture(gesture, new GestureResultCallback(){}, null);
return res;
}
暂时就写到这儿
APP 在下方,欢迎大家测试。有BUG私信告诉我,谢谢。
蚂蚁森林自动手机能量
提取码:0gxf