package com.halzhang.android.examples.accessibilityexample;
import android.accessibilityservice.AccessibilityService;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.halzhang.android.examples.accessibilityexample.models.SettingModel;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* QAS
* Created by Hal on 16/12/16.
*/
public class QQAccessibilityService extends AccessibilityService {
private static final String TAG = "QAS";
private int mCurrentStep = 1;
private String mCurrentQQNum;
private Set<String> mHasSendQqNums = new HashSet<>();
private Set<String> mHasOpenUserInfoNick = new HashSet<>();//此次轮训中已经打开过的qq 昵称
private final static boolean DEBUG = true;
private AtomicBoolean isProcessing = new AtomicBoolean(false);
private Handler handler = new Handler();
private String mGroupTitle;
private int mSendCount;
private int mRecentChatListIndex = 0;
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
int eventType = accessibilityEvent.getEventType();
Log.i(TAG, "onAccessibilityEvent: " + eventType + " step: " + mCurrentStep);
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
switch (mCurrentStep) {
case 1:
openQQGroupChat();
break;
case 2:
openQQGroupSetting();
break;
case 3:
openQQGroupMember();
break;
case 4:
openUserInfoPage();
break;
case 5:
getQQNumberAndOpenChatPage();
break;
case 6:
sendMessageAndBack("hello " + mCurrentQQNum);
break;
default:
break;
}
break;
default:
break;
}
}
/**
* 0-切换 tab
*/
private void switchTab() {
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate: start");
SettingPresenter presenter = new SettingPresenter(getApplicationContext());
SettingModel model = presenter.getSetting();
mGroupTitle = model.getQGroupName();
mSendCount = model.getSendCount();
}
private void reset() {
mCurrentStep = 1;
isProcessing.set(false);
mHasOpenUserInfoNick.clear();
mHasSendQqNums.clear();
}
@Override
public void onDestroy() {
super.onDestroy();
reset();
Log.i(TAG, "onDestroy: end");
}
private void nextStep() {
mCurrentStep++;
}
private void prevStep() {
mCurrentStep--;
}
private void releaseProcess() {
isProcessing.set(false);
}
/**
* 1、打开群组聊天
*/
private void openQQGroupChat() {
if (mHasSendQqNums.size() == mSendCount) {
Log.i(TAG, "openQQGroupChat: send count has enough!");
return;
}
if (mCurrentStep != 1 || isProcessing.get()) {
return;
}
isProcessing.set(true);
AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
if (rootNodeInfo == null) {
releaseProcess();
return;
}
AccessibilityNodeInfo listViewNodeInfo = Utils.getNodeInfoById(rootNodeInfo, "com.tencent.mobileqq:id/recent_chat_list");
if (listViewNodeInfo == null) {
releaseProcess();
return;
}
if (TextUtils.equals("android.widget.AbsListView", listViewNodeInfo.getClassName())) {
int childCount = listViewNodeInfo.getChildCount();
if (mRecentChatListIndex == childCount - 1) {
mRecentChatListIndex = 0;
}
for (int i = mRecentChatListIndex; i < childCount; i++) {
AccessibilityNodeInfo listItemNodeInfo = listViewNodeInfo.getChild(i);
if (TextUtils.equals("android.widget.LinearLayout", listItemNodeInfo.getClassName())) {
Log.i(TAG, "openQQGroupChat: find list item node info");
listItemNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
mRecentChatListIndex = i + 1;
nextStep();
releaseProcess();
return;
}
}
}
// AccessibilityNodeInfo unreadmsgNodeInfo = Utils.getNodeInfoById(rootNodeInfo, "com.tencent.mobileqq:id/unreadmsg");
// if (unreadmsgNodeInfo == null) {
// return;
// }
// AccessibilityNodeInfo listItemNodeInfo = unreadmsgNodeInfo.getParent().getParent().getParent();
// if ("android.widget.LinearLayout".equals(listItemNodeInfo.getClassName())) {
// Log.i(TAG, "openQQGroupChat: find list item node info");
// listItemNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
// mCurrentStep++;
// }
}
/**
* 群聊天页面
* 2、打开群设置页面
*/
private void openQQGroupSetting() {
if (mCurrentStep != 2 || isProcessing.get()) {
return;
}
isProcessing.set(true);
AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
if (rootNodeInfo == null) {
releaseProcess();
return;
}
AccessibilityNodeInfo titleNodeInfo = Utils.getNodeInfoById(rootNodeInfo, "com.tencent.mobileqq:id/title");
if (titleNodeInfo == null) {
releaseProcess();
return;
}
CharSequence title = titleNodeInfo.getText();
Log.i(TAG, "openQQGroupSetting: title: " + title);
if (!TextUtils.equals(mGroupTitle, title)) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
back(true);
prevStep();
releaseProcess();
return;
}
//找到目标群,重置序号
mRecentChatListIndex = 0;
Log.i(TAG, "openQQGroupSetting: find qq group " + mGroupTitle);
AccessibilityNodeInfo groupInfoBtnNodeInfo = Utils.getNodeInfoById(rootNodeInfo, "com.tencent.mobileqq:id/ivTitleBtnRightImage");
if (groupInfoBtnNodeInfo == null) {
releaseProcess();
return;
}
groupInfoBtnNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
nextStep();
releaseProcess();
}
/**
* 3、打开群成员页面,遍历列表,并打开用户资料页面
*/
private void openQQGroupMember() {
if (mCurrentStep != 3 || isProcessing.get()) {
return;
}
isProcessing.set(true);
AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
if (rootNodeInfo == null) {
releaseProcess();
return;
}
AccessibilityNodeInfo addNodeInfo = Utils.getNodeInfoByIdAndText(rootNodeInfo, "com.tencent.mobileqq:id/title", "群成员");
if (addNodeInfo == null) {
releaseProcess();
return;
}
AccessibilityNodeInfo groupMemberNodeInfo = addNodeInfo.getParent();
if (groupMemberNodeInfo != null) {
groupMemberNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
nextStep();
}
releaseProcess();
}
/**
* 4、打开用户资料页面
*/
private synchronized void openUserInfoPage() {
if (mCurrentStep != 4 || isProcessing.get()) {
return;
}
isProcessing.set(true);
AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
if (rootNodeInfo == null) {
releaseProcess();
return;
}
List<AccessibilityNodeInfo> nickNameNodeInfos = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mobileqq:id/tv_name");
if (nickNameNodeInfos == null || nickNameNodeInfos.isEmpty()) {
releaseProcess();
return;
}
for (AccessibilityNodeInfo nickNameNodeInfo : nickNameNodeInfos) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
String nickName = nickNameNodeInfo.getText().toString();
if (nickNameNodeInfo.getParent() == null || nickNameNodeInfo.getParent().getParent() == null) {
Log.w(TAG, "openUserInfoPage: nicknameNodeinfo parent is empty ,nickname: " + nickName);
releaseProcess();
return;
}
AccessibilityNodeInfo listItemNodeInfo = nickNameNodeInfo.getParent().getParent();
if ("android.widget.FrameLayout".equals(listItemNodeInfo.getClassName())) {
if (isFoundMember(listItemNodeInfo)) {
continue;
}
// Log.i(TAG, "openUserInfoPage: check nickname before " + nickName);
if (mHasOpenUserInfoNick.contains(nickName)) {
continue;
}
Log.i(TAG, "openUserInfoPage: nickname: " + nickName);
mHasOpenUserInfoNick.add(nickName);
AccessibilityNodeInfo childNodeInfo = listItemNodeInfo.getChild(0);
// listview 的 NodeInfo 层级比较坑
childNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
nextStep();
releaseProcess();
return;
}
}
final AccessibilityNodeInfo listViewNodeInfo = Utils.getParentByClassName(nickNameNodeInfos.get(0), "android.widget.AbsListView");
releaseProcess();
if (listViewNodeInfo == null) {
return;
}
Log.i(TAG, "openUserInfoPage: all nick has send scroll forward");
listViewNodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
/**
* 是否是管理员或者群主
*
* @return
*/
private boolean isFoundMember(AccessibilityNodeInfo nodeInfo) {
AccessibilityNodeInfo founderNodeInfo = Utils.getNodeInfoByText(nodeInfo, "群主");
AccessibilityNodeInfo adminNodeInfo = Utils.getNodeInfoByText(nodeInfo, "管理员");
return founderNodeInfo != null || adminNodeInfo != null;
}
/**
* 用户资料页面
* 5、获取 QQ 号码,并打开聊天界面
*/
private void getQQNumberAndOpenChatPage() {
// Log.i(TAG, "getQQNumberAndOpenChatPage: " + mCurrentStep);
if (mCurrentStep != 5 || isProcessing.get()) {
return;
}
isProcessing.set(true);
AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
if (rootNodeInfo == null) {
releaseProcess();
return;
}
AccessibilityNodeInfo accountLabelNodeInfo = Utils.getNodeInfoByTextAndClassName(rootNodeInfo, "帐号信息", "android.widget.TextView");
if (accountLabelNodeInfo == null) {
releaseProcess();
return;
}
AccessibilityNodeInfo accountParentNodeInfo = accountLabelNodeInfo.getParent();
if (accountParentNodeInfo == null) {
releaseProcess();
return;
}
if (TextUtils.equals("android.widget.RelativeLayout", accountParentNodeInfo.getClassName())) {
String contentDesc = accountParentNodeInfo.getContentDescription().toString().trim();
String qqNum = parseQQMNumber(contentDesc);
Log.i(TAG, "getQQNumberAndOpenChatPage: " + qqNum);
if (mHasSendQqNums.contains(qqNum)) {
back();
prevStep();
} else {
mCurrentQQNum = qqNum;
AccessibilityNodeInfo sendMessageBtnNodeInfo = Utils.getNodeInfoByTextAndClassName(rootNodeInfo, "发消息", "android.widget.Button");
if (sendMessageBtnNodeInfo == null) {
return;
}
sendMessageBtnNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
nextStep();
}
}
releaseProcess();
}
private String parseQQMNumber(CharSequence source) {
final Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(source);
if (matcher.find()) {
return matcher.group();
}
return null;
}
/**
* 6、发消息,并返回
*
* @param message
*/
private void sendMessageAndBack(String message) {
if (mCurrentStep != 6 || isProcessing.get()) {
return;
}
isProcessing.set(true);
AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
if (rootNodeInfo == null) {
releaseProcess();
return;
}
AccessibilityNodeInfo inputNodeInfo = Utils.getNodeInfoById(rootNodeInfo, "com.tencent.mobileqq:id/input");
if (inputNodeInfo == null) {
releaseProcess();
return;
}
if (TextUtils.equals("android.widget.EditText", inputNodeInfo.getClassName())) {
Utils.setText(this, inputNodeInfo, message);
final AccessibilityNodeInfo sendBtnNodeInfo = Utils.getNodeInfoById(rootNodeInfo, "com.tencent.mobileqq:id/fun_btn");
if (sendBtnNodeInfo == null) {
releaseProcess();
return;
}
if (TextUtils.equals("android.widget.Button", sendBtnNodeInfo.getClassName())) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO: 16/12/26 测试中先注释
// sendBtnNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
Log.i(TAG, "run: " + mCurrentQQNum + " send!");
mHasSendQqNums.add(mCurrentQQNum);
mCurrentStep = 1;
back();
releaseProcess();
}
}, 1000);
} else {
releaseProcess();
}
}
}
private void back() {
back(false);
}
private void back(boolean immediately) {
if (immediately) {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); // 返回
} else {
handler.postDelayed(new Runnable() { // 返回主界面,这里延迟执行,为了有更好的交互
@Override
public void run() {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); // 返回
}
}, 500);
}
}
@Override
public void onInterrupt() {
}
}