实例介绍
【实例简介】免Root!
【实例截图】
【核心代码】
/*
* Copyright (c) 2017.
* qsboy.com 版权所有
*/
package com.qiansheng.messagecapture;
import android.accessibilityservice.AccessibilityService;
import android.graphics.Rect;
import android.os.Handler;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import static com.qiansheng.messagecapture.Debug.ServerOnConnected;
import static com.qiansheng.messagecapture.MainActivity.File_Withdraw;
import static com.qiansheng.messagecapture.XBitmap.getImageFileInQQ;
/**
* 防撤回神器 主要代码
* 使用了安卓的 辅助功能类 AccessibilityService
* 所有的高权限的处理都在这里完成
* 这个类本是Google设计为盲人或者视觉障碍服务的,使他们也能用手机
* (国外对残疾人的关爱真是很到位)
* 在经过一系列配置之后,我就能通过这个类来获取屏幕,以及通知栏信息了
* 我把截获的信息按名称保存到文件中,再在有撤回的时候回去查找
* 主要技术点:
* Search类里的系列方法, 我底下的注释已经很详细了
*/
public class MessageCaptor extends AccessibilityService {
final String TAG = "MessageCaptor";
final String NameID_qq = "com.tencent.mobileqq:id/title";
final String TEXT_WITHDRAW = "撤回了一条消息";
static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm MM/dd", Locale.CHINA);
List<String> WD_MsgList = XListAdapter.MsgList;
List<String> WD_NameList = XListAdapter.NameList;
Set<String> QQ_NameList;
boolean is_wx;
String tempMessage;
long ClickTime = 0;
long ClickTime2 = 0;
long ClickTime3 = 0;
Handler mHandler;
SingleClick singleClick;
DoubleClick doubleClick;
TrebleClick trebleClick;
AddNewMessage addNewMessage;
XFile xFile;
@Override
protected void onServiceConnected() {
ServerOnConnected = true;
xFile = new XFile(this);
QQ_NameList = getNameList();
mHandler = new Handler();
}
@Override
public void onInterrupt() {
ServerOnConnected = false;
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
int eventType = event.getEventType();
AccessibilityNodeInfo nodeInfo = event.getSource();
is_wx = event.getPackageName().equals("com.tencent.mm");
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
//在屏幕切换时,如果用户是第一次使用app,则推送一条表示成功的通知
if (xFile.isShowCheckedNotice())
new XNotification(this).printSuccess();
break;
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
//顶部通知栏状态改变
getNotification(event);
break;
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
if (nodeInfo == null)
return;
//只需在改变类型为文字时执行添加操作
//大部分change type为 CONTENT_CHANGE_TYPE_SUBTREE
int types = event.getContentChangeTypes();
if (types != AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT)
break;
CharSequence cs = nodeInfo.getText();
if (cs == null)
break;
Log.w(TAG, "Text Changed : " cs);
//判断是不是QQ聊天时其他人发的消息
if (isOtherConversation(cs))
break;
//添加新消息至本地文件
addNewMessage = new AddNewMessage();
mHandler.post(addNewMessage);
break;
case AccessibilityEvent.TYPE_VIEW_CLICKED:
//点击事件
if (nodeInfo == null)
break;
if (nodeInfo.getText() == null)
break;
//只有点击了"撤回一条消息"才会继续执行
if (!nodeInfo.getText().toString().contains(TEXT_WITHDRAW)) {
//test
// new GetNodes();
break;
}
String name = getName();
//处理点击事件,单击双击等
onClick(event, name);
break;
}
}
/**
* 查找的主函数
*/
class Search {
Date start = new Date();
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
//读屏幕的变量
AccessibilityNodeInfo n1;
AccessibilityNodeInfo n2;
AccessibilityNodeInfo n3;
int childCount;
String contentScreen;
/**
* 自己的文件类
* 有 输出前后一行 等方法
*/
XFile.Search search;
//search的变量
List<String> aroundSting;
String line;
String content;
String target; //撤回前后的内容
int size; //要找的数量
int num = 0; //连续撤回的数量
boolean flag = false; //是正着扫还是反着扫
List<String> screenList = new ArrayList<>();
List<String> listMsg = new ArrayList<>();
//给getScreen调用的无参构造方法
Search() {
}
Search(String name) {
Log.i(TAG, "Searching...");
if (name == null) {
Log.e(TAG, "name is null !");
XToast.makeText(getApplicationContext(),
"无法获取联系人名字 请\nshutdown软件\n打开辅助功能\n打开软件").show();
return;
}
try {
search = new XFile.Search(MainActivity.File_Dir name);
} catch (IOException e) {
XToast.makeText(getApplicationContext(),
"打开软件之前的消息是看不到的").show();
e.printStackTrace();
return;
}
aroundSting = getPreString();
size = aroundSting.size();
Log.d(TAG, "size: " size);
/**
* 从列表右边开始扫 就是先找撤回消息的前一句再根据这个找下一句
*/
flag = false;
for (int i = size - 1; i >= 0; i--)
scan(i);
/**
* 如果有没找到就换个方向扫 就是先找撤回消息的后一句再根据这个找前一句
*/
if (listMsg.size() == 0 || num > 0) {
search.seekEnd();
num = 0;
flag = true;
Log.e(TAG, "scan from bottom");
aroundSting = getAftString();
size = aroundSting.size();
for (int i = 0; i < size; i )
scan(i);
}
/**
* 如果还没找到,就从最近写入的地方读一条出来
* 最多从最近添加的两条里面找
*/
if (listMsg.size() == 0 || num > 0) {
Log.w(TAG, "still not found");
search.seekEnd();
for (int i = 0; i < 2; ) {
line = search.nextLine();
content = getContent(line);
Log.i(TAG, "content: " content);
if (content.contains(TEXT_WITHDRAW))
continue;
i ;
if (!screenList.contains(content)) {
addToListMsg();
num--;
break;
}
}
}
/**
* 如果还是没找全的话
* 提醒不能对面整屏的撤回
*/
if (listMsg.size() == 0 || num > 0) {
if (screenList.size() == 0) {
line = "屏幕内必须有对方说过的一句话\n/请不要暴力测试"
new Date().getTime();
addToListMsg();
}
}
/**
* 最后集中写入文件
*/
if (listMsg.size() > 0) {
List<String> addedList = new ArrayList<>();
WD_MsgList = XListAdapter.MsgList;
int size = 0;
String addedString = null;
for (String msg : listMsg)
if (!WD_MsgList.contains(getContent(msg))) {
Log.i(TAG, msg " " getContent(msg));
xFile.writeFile(msg '#' name, File_Withdraw);
addedList.add(msg);
size ;
} else addedString = getContent(msg);
if (size == 1)
XToast.makeText(getApplicationContext(), getContent(addedList.get(0))).show();
else if (size > 1)
XToast.makeText(getApplicationContext(), "撤回了多条消息\n请在软件里查看").show();
else XToast.makeText(getApplicationContext(), addedString).show();
} else
XToast.makeText(getApplicationContext(), "sorry 并没有截到消息\n可在帮助中查看原因").show();
xFile.refresh(); //刷新撤回消息列表
search.closeFile();
Date end = new Date();
Log.w(TAG, "searching cost " (end.getTime() - start.getTime()) " mm");
}
void scan(int i) {
target = aroundSting.get(i);
//把换行变成空格
target = xFile.format(target);
Log.i(TAG, "i: " i);
Log.w(TAG, "target : " target);
//连续撤回的次数 QQ是null 微信是文字_某某撤回了一条消息
if (target == null || (target.contains(TEXT_WITHDRAW))) {
num ;
} else {
Log.i(TAG, "num:" num);
while (true) {
line = search.nextLine(); //往下找
if (line == null) //找完了 没找到
return;
content = getContent(line); //提取一行中的内容
if (content == null)
continue;
if (target.equals(content)) { //匹配到了list里的内容
Log.w(TAG, "search: FOUND " target);
if (flag) //如果找的是后一句
line = search.nextLine(); //就找前一句
else //如果找的是前一句
line = search.preLine(); //就找下一句
Log.i(TAG, "read : " line);
if (line == null) { //这种情况发生在target被刚写入的
search.nextLine();
continue; //这一行可能是之后滚屏加进来的
}
if (aroundSting.contains(getContent(line))) {
Log.w(TAG, "Screen List Contains this content , Continue");
continue;
}
content = getContent(line);
Log.e(TAG, "撤回的消息是: " content);
addToListMsg();
//连续撤回
if (num > 0) {
Log.i(TAG, "number > 0");
screenList = getScreen();
//加个偏置
if (flag)
line = search.preLine();
else
search.nextLine();
while (true) {
if (flag)
line = search.nextLine();
else
line = search.preLine();
if (line == null) {
search.nextLine();
break;
}
content = getContent(line);
if (screenList.contains(content))
continue;
addToListMsg();
if (num == 0)
return;
num--;
}
} else break;
}
}
}
}
void addToListMsg() {
//如果这条是刚刚加过的(比如之前正向的scan)
if (listMsg.contains(line))
//但如果是两张图片的几率还是挺大的需要保留
if (!content.equals("[图片]"))
return;
//如果是图片的话把从QQ缓存里找来的图片保存到自己的文件夹下
if (content.equals("[图片]")) {
long time = getTime_Long(line);
boolean b = getImageFileInQQ(time);
if (b)
line = "#image" time getTime_String(line);
else line = "该图片曾经发过\n所以无法找到" getTime_String(line);
}
//有时候getContent报错了就没有加时间会导致解析错误
if (line.length() < 13)
line = new Date().getTime();
listMsg.add(line);
Log.w(TAG, "add: " line);
}
/**
* 由于微信控件的ID会经常变
* 所以不能直接用nodeInfo.findAccessibilityNodeInfosByViewId(resourceID);
* 所以我的解决方法是通过解析布局,通过根布局慢慢getChild
* id好改,布局就不好改了
* 找出来的内容只有对方发送的(可以加上自己发送的但没必要)
*
* @return ScreenList
*/
List<String> getScreen() {
List<String> screenList = new ArrayList<>();
try {
if (is_wx) {
try {
n1 = nodeInfo.getChild(0).getChild(0).getChild(4);
getScreenList_wx(screenList, n1);
} catch (Exception ignored) {
}
if (screenList.size() == 0) {
n1 = nodeInfo.getChild(8).getChild(0).getChild(4);
getScreenList_wx(screenList, n1);
}
} else {
try {
n1 = nodeInfo.getChild(5);
getScreenList_qq(screenList, n1);
} catch (Exception ignored) {
}
if (screenList.size() == 0) {
n1 = nodeInfo.getChild(4);
getScreenList_qq(screenList, n1);
}
// //通过ID查找消息,但测试出来有时候会找不全
// String resourceID = "com.tencent.mobileqq:id/chat_item_content_layout";
// List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(resourceID);
// for (AccessibilityNodeInfo text : list) {
// content = text.getText().toString();
// screenList.add(content);
// }
}
} catch (Exception e) {
e.printStackTrace();
}
// TODO: test
if (screenList.size() == 0)
new GetNodes();
Log.w(TAG, "Screen List is: " screenList);
return screenList;
}
/**
* 先找到"某某撤回一条消息"然后把这之 前 的内容抓下来存入List
*
* @return Item before withdraw
*/
List<String> getPreString() {
List<String> preSting = new ArrayList<>();
try {
if (is_wx) {
try {
n1 = nodeInfo.getChild(0).getChild(0).getChild(4);
getPreString_wx(preSting, n1);
} catch (Exception ignored) {
}
if (preSting.size() == 0) {
n1 = nodeInfo.getChild(8).getChild(0).getChild(4);
getPreString_wx(preSting, n1);
}
} else {
// QQ
try {
n1 = nodeInfo.getChild(5);
getPreString_qq(preSting, n1);
} catch (Exception ignored) {
}
if (preSting.size() == 0) {
n1 = nodeInfo.getChild(4);
getPreString_qq(preSting, n1);
}
}
} catch (Exception ignored) {
}
if (preSting.size() == 0)
new GetNodes();
Log.w(TAG, "pre String List is : " preSting);
return preSting;
}
/**
* 先找到"某某撤回一条消息"然后把这之 后 的内容抓下来存入List
* 用于 撤回的前一句找不到的情况
*
* @return Item after withdraw
*/
List<String> getAftString() {
List<String> aftSting = new ArrayList<>();
try {
if (is_wx) {
try {
n1 = nodeInfo.getChild(0).getChild(0).getChild(4);
getAftString_wx(aftSting, n1);
} catch (Exception ignored) {
}
if (aftSting.size() == 0) {
n1 = nodeInfo.getChild(8).getChild(0).getChild(4);
getAftString_wx(aftSting, n1);
}
} else {
// QQ
try {
n1 = nodeInfo.getChild(5);
getAftString_qq(aftSting, n1);
} catch (Exception ignored) {
}
if (aftSting.size() == 0) {
n1 = nodeInfo.getChild(4);
getAftString_qq(aftSting, n1);
}
}
} catch (Exception ignored) {
}
if (aftSting.size() == 0)
new GetNodes();
Log.w(TAG, "after String List is : " aftSting);
return aftSting;
}
void getScreenList_wx(List<String> screenList, AccessibilityNodeInfo n1) {
for (int i = 0; i < n1.getChildCount(); i ) {
n2 = n1.getChild(i);
childCount = n2.getChildCount();
if (childCount != 0) {
n3 = n2.getChild(childCount - 1);
if (n3.getText() != null) {
content = n3.getText().toString();
screenList.add(content);
} else if (n3.getChildCount() == 3) {
CharSequence charSequence = n3.getChild(2).getText();
if (charSequence == null)
continue;
if (charSequence.toString().contains("红包")) {
// TODO: 抢红包
Log.e(TAG, "收到红包 !");
}
}
}
}
}
void getScreenList_qq(List<String> screenList, AccessibilityNodeInfo n1) {
for (int i = 0; i < n1.getChildCount(); i ) {
n2 = n1.getChild(i);
childCount = n2.getChildCount();
if (childCount != 0) {
n3 = n2.getChild(childCount - 1);
if (n3.getText() != null) {
content = n3.getText().toString();
screenList.add(content);
} else if (n3.getClassName().equals("android.widget.RelativeLayout")) {
//判断是不是红包
if (n3.getChildCount() == 3) {
CharSequence charSequence = n3.getChild(2).getText();
if (charSequence == null)
continue;
if (charSequence.toString().contains("红包")) {
// TODO: 抢红包
Log.e(TAG, "收到红包 !");
// getHongBao(n3);
}
} else {
content = "[图片]";
screenList.add(content);
}
}
}
}
}
void getPreString_wx(List<String> preSting, AccessibilityNodeInfo n1) {
String tempSting = null;
for (int i = 0; i < n1.getChildCount(); i ) {
n2 = n1.getChild(i);
childCount = n2.getChildCount();
if (childCount != 0) {
n3 = n2.getChild(childCount - 1);
if (n3.getText() != null) {
contentScreen = n3.getText().toString();
if (contentScreen.contains(TEXT_WITHDRAW))
preSting.add(tempSting);
tempSting = contentScreen;
}
}
}
}
void getPreString_qq(List<String> preSting, AccessibilityNodeInfo n1) {
String tempSting = null;
for (int i = 0; i < n1.getChildCount(); i ) {
n2 = n1.getChild(i);
childCount = n2.getChildCount();
if (childCount != 0) {
n3 = n2.getChild(childCount - 1);
if (n3.getText() != null) {
content = n3.getText().toString();
if (content.contains(TEXT_WITHDRAW)) {
preSting.add(tempSting);
tempSting = null;
} else
tempSting = content;
} else if (n3.getClassName().equals("android.widget.RelativeLayout"))
tempSting = "[图片]";
}
}
}
void getAftString_wx(List<String> aftSting, AccessibilityNodeInfo n1) {
boolean flag = false;
for (int i = 0; i < n1.getChildCount(); i ) {
n2 = n1.getChild(i);
childCount = n2.getChildCount();
if (childCount != 0) {
n3 = n2.getChild(childCount - 1);
if (n3.getText() != null) {
content = n3.getText().toString();
if (flag)
aftSting.add(content);
if (content.contains(TEXT_WITHDRAW))
flag = true;
}
}
}
}
void getAftString_qq(List<String> aftString, AccessibilityNodeInfo n1) {
boolean flag = false;
for (int i = 0; i < n1.getChildCount(); i ) {
n2 = n1.getChild(i);
childCount = n2.getChildCount();
if (childCount != 0) {
n3 = n2.getChild(childCount - 1);
if (flag) {
if (n3.getText() != null) {
content = n3.getText().toString();
aftString.add(content);
flag = false;
} else if (n3.getClassName().equals("android.widget.RelativeLayout")) {
aftString.add("[图片]");
flag = false;
}
}
if (n3.getText() != null) {
content = n3.getText().toString();
if (content.contains(TEXT_WITHDRAW))
flag = true;
}
}
}
}
}
void getHongBao(AccessibilityNodeInfo nodeInfo) {
new GetNodes(nodeInfo);
if (!is_wx) {
CharSequence text = nodeInfo.getChild(1).getText();
if (text == null || text.toString().equals("已拆开")) {
Log.e(TAG, "已拆开 !");
return;
} else Log.w(TAG, "text : " text);
}
if (!nodeInfo.isClickable()) {
Log.e(TAG, "unClickable ! ");
return;
}
if (!nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK)) {
Log.e(TAG, "click failed !");
return;
}
if (is_wx) {
if (nodeInfo.getChildCount() != 5) {
Log.e(TAG, "wx : childCount != 5 !");
return;
}
AccessibilityNodeInfo btn = nodeInfo.getChild(3);
if (!btn.isClickable()) {
Log.e(TAG, "wx : child unClickable !");
return;
}
btn.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
/**
* 处理点击事件 单击多击
* 我这边两次点击时间差为300毫秒
*/
private void onClick(AccessibilityEvent event, String name) {
ClickTime3 = ClickTime2;
ClickTime2 = ClickTime;
ClickTime = event.getEventTime();
if ((ClickTime - ClickTime3) < 600) {
//三击 先取消双击单击的post
if (doubleClick != null)
mHandler.removeCallbacks(doubleClick);
if (singleClick != null)
mHandler.removeCallbacks(singleClick);
trebleClick = new TrebleClick(name);
mHandler.post(trebleClick);
//防止连按四下多次执行三击操作
ClickTime3 = 0;
} else if ((ClickTime - ClickTime2) < 300) {
//双击 先取消单击的post
if (singleClick != null)
mHandler.removeCallbacks(singleClick);
doubleClick = new DoubleClick(name);
mHandler.postDelayed(doubleClick, 300);
} else {
//单击
singleClick = new SingleClick(name);
mHandler.postDelayed(singleClick, 300);
}
}
/**
* 单击
* 判断撤回消息列表里是否存在当前的聊天对象 如果有,就直接输出
* 如果没有,就查找
*/
class SingleClick implements Runnable {
String name;
SingleClick(String name) {
this.name = name;
}
@Override
public void run() {
Log.w(TAG, "Single Click");
if (WD_NameList.contains(name)) {
String text = WD_MsgList.get(WD_NameList.indexOf(name));
Log.w(TAG, "text : " text);
XToast.makeText(getApplicationContext(), text).show();
} else {
new Search(name);
}
}
}
/**
* 双击
* 直接查找
*/
class DoubleClick implements Runnable {
String name;
DoubleClick(String name) {
this.name = name;
}
@Override
public void run() {
Log.e(TAG, "Double Click");
new Search(name);
}
}
/**
* 三击
* 删除当前联系人加入的最后一行消息
* 在滚屏和切换窗口时会多加消息
* 主要是调试用
*/
class TrebleClick implements Runnable {
String name;
TrebleClick(String name) {
this.name = name;
}
@Override
public void run() {
Log.e(TAG, "TREBLE CLICKED");
new XFile.RemoveLine(name, getApplicationContext()).remove();
}
}
/**
* 往本地写内容
*/
class AddNewMessage implements Runnable {
@Override
public void run() {
try {
List<String> list = new Search().getScreen();
if (list.size() < 1) {
Log.d(TAG, "Screen List is Empty, return");
return;
}
String item = list.get(list.size() - 1);
Log.w(TAG, "MESSAGE IS " item);
//判断是不是刚刚加过的 这边偷懒了没有去文件里查找确认
//微信会在滚屏时加入大量历史消息
if (item.equals(tempMessage)) {
//4.0.3:
// 图片相同的可能性很大
// if (!item.equals("[图片]")) {
Log.d(TAG, "Equal to Last Msg, return");
return;
// }
}
if (item.contains(TEXT_WITHDRAW)) {
Log.i(TAG, "Contains 撤回了一条消息 , return");
return;
}
//给消息加上时间戳
long date = new Date().getTime();
//4.0.3 保存的时间为格式化好的, 由于该格式只精确到分,查找图片的精度不够
//所以改为在显示的时候再加sdf.format
//String line = item sdf.format(date);
String line = item date;
tempMessage = item;
String name = getName();
if (!QQ_NameList.contains(name)) {
QQ_NameList.add(name);
Log.w(TAG, "add new name");
}
xFile.writeFile(line, name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 判断是否是在其他人的聊天界面收到了消息
* 为了在 QQ-不是当前联系人-发来消息 时检查是否出现过这个人
* QQ比较重,会在当前屏幕生成一个内部的弹窗
* 这种消息我截下来和普通消息一样,只是内容是这样的形式:
* "Name" ' : ' "Message"
* 我根据这里是否存在冒号
* 然后判断Name是否在NameList中来区分 QQ-普通消息和别人发的消息
* 但微信不一样,只要是不在当前聊天窗口发来的消息都会给Notification
*/
private boolean isOtherConversation(CharSequence cs) {
String string = cs.toString();
int len = cs.length();
int index1 = string.indexOf(":");
if (index1 > 0) {
if (len - index1 == 3) //是时间
return true;
String name = string.substring(0, index1);
Log.i(TAG, "name: " name);
//如果在联系人列表里出现过的,那么就是在其他人的聊天界面
if (QQ_NameList.contains(name)) {
String content = string.substring(index1 1);
long date = new Date().getTime();
String line = content date;
xFile.writeFile(line, name);
return true;
} else {
//判断是不是群消息
int index2 = string.indexOf("-");
if (index2 > 0) {
name = string.substring(0, index2);
Log.i(TAG, "name: " name);
if (QQ_NameList.contains(name)) {
String content = string.substring(index1 1);
long date = new Date().getTime();
String line = content date;
xFile.writeFile(line, name);
return true;
}
}
}
}
return false;
}
/**
* 把已经存下来的名字拉到一个Set里
*
* @return Known Name List
*/
Set<String> getNameList() {
Set<String> nameList = new HashSet<>();
File fileDir = getFilesDir();
for (File file : fileDir.listFiles()) {
if (file.isFile())
nameList.add(file.getName());
}
return nameList;
}
/**
* 把通知栏里截获的消息处理并写入本地
*/
void getNotification(AccessibilityEvent event) {
Log.i(TAG, "Notification Changed");
List<CharSequence> texts = event.getText();
if (texts.isEmpty() || texts.size() == 0)
return;
for (CharSequence text : texts) {
if (text == null)
return;
String string = text.toString();
Log.w(TAG, "Notification Text:" string);
if (string.equals("你的帐号在电脑登录"))
return;
String content;
String name;
int i = string.indexOf(':');
if (i < 1) {
Log.d(TAG, "Notification does not contains ':'");
return;
}
name = string.substring(0, i);
content = string.substring(i 2);
//是QQ群消息
if (!is_wx)
if (name.charAt(i - 1) == ')' && name.contains("(")) {
content = string.substring(i 1);
name = name.substring(name.indexOf('(') 1, name.indexOf(')'));
}
long date = new Date().getTime();
Log.w(TAG, "name : " name " content : " content " time : " date);
String line = content date;
tempMessage = content;
xFile.writeFile(line, name);
}
}
/**
* 根据UI解析出屏幕中Name
*
* @return Name
*/
String getName() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
String s = "";
if (is_wx) {
try {
s = nodeInfo.getChild(0).getChild(0).getChild(1).getText().toString();
} catch (Exception e) {
try {
s = nodeInfo.getChild(8).getChild(0).getChild(1).getText().toString();
} catch (Exception ignored) {
}
}
} else {
try {
List<AccessibilityNodeInfo> qq = nodeInfo.findAccessibilityNodeInfosByViewId(NameID_qq);
s = qq.get(0).getText().toString();
} catch (Exception ignored) {
}
}
if (s.length() != 0) {
Log.w(TAG, "name : " s);
return s;
} else {
new GetNodes();
Log.e(TAG, "Get Name ERROR !");
return null;
}
}
/**
* 把自己存入本地的"line"
* 格式为 Content Time
* 中的 Content分离出来
*
* @param line line in file
* @return content
*/
String getContent(String line) {
try {
return line.substring(0, line.length() - 13);
} catch (Exception e) {
e.printStackTrace();
return "Error";
}
}
/**
* 把Time分离出来
* 得到的是字符串 12:34 02/18
*
* @param line line in file
* @return time
*/
String getTime_String(String line) {
return line.substring(line.length() - 13);
}
/**
* 把Time分出来
* 并sdf.parse
* 把String类型的time转换成Long的time
* 为了能够查找QQ撤回的图片
* 因为QQ图片文件名是根据第一次收到的时间命名的
* 之后的图片只会生成一个链
* 所以QQ只能查看第一次发的图片
*
* @param line line in file
* @return time
*/
long getTime_Long(String line) {
//4.0.3
// try {
// Date date = sdf.parse(line.substring(line.length() - 11));
// return date.getTime();
// } catch (ParseException e) {
// e.printStackTrace();
// }
String substring = line.substring(line.length() - 13);
return Long.parseLong(substring);
}
/**
* 调试工具
* 用于输出屏幕的node信息
*/
class GetNodes {
String print(AccessibilityNodeInfo nodeInfo) {
CharSequence text = nodeInfo.getText();
CharSequence description = nodeInfo.getContentDescription();
CharSequence packageName = nodeInfo.getPackageName();
CharSequence className = nodeInfo.getClassName();
boolean focusable = nodeInfo.isFocusable();
boolean clickable = nodeInfo.isClickable();
Rect rect = new Rect();
nodeInfo.getBoundsInScreen(rect);
return "| "
"text: " text " \t"
"description: " description " \t"
"location: " rect " \t"
"package name: " packageName " \t"
"class name: " className " \t"
"focusable: " focusable " \t"
"clickable: " clickable " \t"
'\n';
}
//无参就打印根布局
GetNodes() {
AccessibilityNodeInfo n0 = getRootInActiveWindow();
show(n0);
}
//传了参数就只打印这个节点下的所有自节点
GetNodes(AccessibilityNodeInfo n) {
show(n);
}
private void show(AccessibilityNodeInfo n) {
try {
Log.w(TAG, "\nv0 " print(n));
int v1 = n.getChildCount();
for (int i1 = 0; i1 < v1; i1 ) {
AccessibilityNodeInfo n1 = n.getChild(i1);
Log.w(TAG, "\n v1: " i1 " " print(n1));
int v2 = n1.getChildCount();
for (int i2 = 0; i2 < v2; i2 ) {
AccessibilityNodeInfo n2 = n1.getChild(i2);
Log.w(TAG, "\n v2: " i2 " " print(n2));
int v3 = n2.getChildCount();
for (int i3 = 0; i3 < v3; i3 ) {
AccessibilityNodeInfo n3 = n2.getChild(i3);
Log.w(TAG, "\n v3: " i3 " " print(n3));
int v4 = n3.getChildCount();
for (int i4 = 0; i4 < v4; i4 ) {
AccessibilityNodeInfo n4 = n3.getChild(i4);
Log.w(TAG, "\n v4: " i4 " " print(n4));
int v5 = n4.getChildCount();
for (int i5 = 0; i5 < v5; i5 ) {
AccessibilityNodeInfo n5 = n4.getChild(i5);
Log.w(TAG, "\n v5: " i5 " " print(n5));
int v6 = n5.getChildCount();
for (int i6 = 0; i6 < v6; i6 ) {
AccessibilityNodeInfo n6 = n5.getChild(i6);
Log.w(TAG, "\n v6: " i6 " " print(n6));
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
好例子网口号:伸出你的我的手 — 分享!
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明


网友评论
我要评论