下面列出了android.view.accessibility.AccessibilityNodeInfo#getText ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
public boolean checkCriteria(AccessibilityNodeInfo node) {
String text = "";
String desc = "";
if (node.getText() != null) {
text = node.getText().toString();
}
if (node.getContentDescription() != null) {
desc = node.getContentDescription().toString();
}
if (this.checkValue != null) {
return text.equals(this.checkValue) || desc.equals(this.checkValue);
} else if (this.checkPattern != null) {
return this.checkPattern.matcher(text).matches() || this.checkPattern.matcher(desc).matches();
}
return false;
}
private static void logNodeHierachy(AccessibilityNodeInfo nodeInfo, int depth) {
Rect bounds = new Rect();
nodeInfo.getBoundsInScreen(bounds);
StringBuilder sb = new StringBuilder();
if (depth > 0) {
for (int i=0; i<depth; i++) {
sb.append(" ");
}
sb.append("\u2514 ");
}
sb.append(nodeInfo.getClassName());
sb.append(" (" + nodeInfo.getChildCount() + ")");
sb.append(" " + bounds.toString());
if (nodeInfo.getText() != null) {
sb.append(" - \"" + nodeInfo.getText() + "\"");
}
Log.v(TAG, sb.toString());
for (int i=0; i<nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo childNode = nodeInfo.getChild(i);
if (childNode != null) {
logNodeHierachy(childNode, depth + 1);
}
}
}
/**
* 描述:处理新消息
* 作者:卜俊文
* 邮箱:[email protected]
* 日期:2017/11/3 下午11:21
*/
private void progressNewMessage(AccessibilityEvent event) {
if (event == null) {
return;
}
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
//根据event的source里的text,来判断这个消息是否包含[QQ红包]的字眼,有的话就跳转过去
CharSequence text = source.getText();
if (!TextUtils.isEmpty(text) && text.toString().contains(QQConstant.QQ_ENVELOPE_KEYWORD)) {
performViewClick(source);
}
}
/**
* @param nodeInfo
* @param accessibilityEvent
* @return If from notification ,msg format :{@link Constant#MONITOR_TAG} + ":real QQ No: "+{@link Constant#MONITOR_CMD_VIDEO}
*/
private String retrieveQQNumber(AccessibilityNodeInfo nodeInfo, AccessibilityEvent accessibilityEvent) {
if (accessibilityEvent.getEventType() == TYPE_NOTIFICATION_STATE_CHANGED) {
Parcelable data = accessibilityEvent.getParcelableData();
if (data instanceof Notification) {
if (((Notification) data).tickerText != null) {
return ((Notification) data).tickerText.toString().split(":")[1];
}
}
} else {
List<AccessibilityNodeInfo> nodeInfos = nodeInfo.findAccessibilityNodeInfosByText(MONITOR_TAG);
if (!AppUtils.isListEmpty(nodeInfos)) {
String tag;
for (AccessibilityNodeInfo info : nodeInfos) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "retrieveQQNumber: " + info.getText());
}
tag = (String) info.getText();
if (!TextUtils.isEmpty(tag) && tag.contains(MONITOR_TAG)) {
return tag.substring(Constant.MONITOR_TAG.length());
}
}
}
}
return Privacy.QQ_NUMBER;
}
private void findChildView(AccessibilityNodeInfo info, String findText) {
String text = info.getText() + "";
boolean isContentTxl = text.equals(findText);
if (info.getChildCount() == 0) {
if (!TextUtils.isEmpty(text) && isContentTxl) {
L.e("Text:" + text + "是否" + isContentTxl + "是否2" + text.equals("通讯录"));
performClick(info);
}
} else {
for (int i = 0; i < info.getChildCount(); i++) {
if (info.getChild(i) != null) {
findChildView(info.getChild(i), findText);
}
}
}
}
/**
* Clears the existing text contents in an editable field.
*
* The {@link UiSelector} of this object must reference a UI element that is editable.
*
* When you call this method, the method sets focus on the editable field, selects all of its
* existing content, and clears it by sending a DELETE key press
*
* @throws UiObjectNotFoundException
* @since API Level 16
*/
public void clearTextField() throws UiObjectNotFoundException {
Tracer.trace();
// long click left + center
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
if(node == null) {
throw new UiObjectNotFoundException(mUiSelector.toString());
}
CharSequence text = node.getText();
// do nothing if already empty
if (text != null && text.length() > 0) {
if (UiDevice.API_LEVEL_ACTUAL > Build.VERSION_CODES.KITKAT) {
setText("");
} else {
Bundle selectionArgs = new Bundle();
// select all of the existing text
selectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
selectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
text.length());
boolean ret = node.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
if (!ret) {
Log.w(LOG_TAG, "ACTION_FOCUS on text field failed.");
}
ret = node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, selectionArgs);
if (!ret) {
Log.w(LOG_TAG, "ACTION_SET_SELECTION on text field failed.");
}
// now delete all
getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
}
}
}
/**
* Set the text content by sending individual key codes.
* @hide
*/
public void legacySetText(String text) {
AccessibilityNodeInfo node = getAccessibilityNodeInfo();
// Per framework convention, setText(null) means clearing it
if (text == null) {
text = "";
}
CharSequence currentText = node.getText();
if (!text.equals(currentText)) {
InteractionController ic = mDevice.getAutomatorBridge().getInteractionController();
// Long click left + center
Rect rect = getVisibleBounds();
ic.longTapNoSync(rect.left + 20, rect.centerY());
// Select existing text
mDevice.wait(Until.findObject(By.descContains("Select all")), 50).click();
// Wait for the selection
SystemClock.sleep(250);
// Delete it
ic.sendKey(KeyEvent.KEYCODE_DEL, 0);
// Send new text
ic.sendText(text);
}
}
private static String print(AccessibilityNodeInfo nodeInfo) {
if (nodeInfo == null)
return "";
CharSequence text = nodeInfo.getText();
CharSequence description = nodeInfo.getContentDescription();
CharSequence className = nodeInfo.getClassName();
CharSequence packageName = nodeInfo.getPackageName();
boolean focusable = nodeInfo.isFocusable();
boolean clickable = nodeInfo.isClickable();
Rect rect = new Rect();
nodeInfo.getBoundsInScreen(rect);
String viewId = nodeInfo.getViewIdResourceName();
return "| "
+ "text: " + text + " \t"
+ "description: " + description + " \t"
+ String.format("%-40s", "ID: " + viewId) + " \t"
+ String.format("%-40s", "class: " + className) + " \t"
+ String.format("%-30s", "location: " + rect) + " \t"
// + "focusable: " + focusable + " \t"
// + "clickable: " + clickable + " \t"
// + String.format("%-30s", "package: " + packageName) + " \t"
;
}
/**
* performClickActionWithFindNode.
* @param aAccessibilityNodeInfo aAccessibilityNodeInfo
* @param aClassName aClassName
* @param aNodeTxt aNodeTxt
* @param isGlobal isGlobal
* @return true
*/
private boolean performClickActionWithFindNode(AccessibilityNodeInfo aAccessibilityNodeInfo, String aClassName, String aNodeTxt, boolean isGlobal) {
if(aAccessibilityNodeInfo == null) {
return false;
} else {
List<AccessibilityNodeInfo> targetList = aAccessibilityNodeInfo.findAccessibilityNodeInfosByText(aNodeTxt);
if (targetList != null) {
for (AccessibilityNodeInfo targetNode : targetList) {
if (aClassName != null) {
String targetClassName = targetNode.getClassName() == null ? "" : targetNode.getClassName().toString();
if (!aClassName.equals(targetClassName)) {
continue;
}
}
String targetNodeText = targetNode.getText() == null ? "" : targetNode.getText().toString();
if (!aNodeTxt.equals(targetNodeText)) {
continue;
}
if (isGlobal && !isAutoRunning) {
performGlobalAction(AccessibilityNodeInfo.ACTION_FOCUS);
} else {
performClickAction(targetNode);
}
return true;
}
}
}
return false;
}
private void compute_feature_vector(Hashtable<String, FeatureValuePair> map, AccessibilityNodeInfo node, int level) {
int child_cnt = node.getChildCount();
if (child_cnt > 0) {
for (int i = 0; i < child_cnt; i++) {
AccessibilityNodeInfo child = node.getChild(i);
compute_feature_vector(map, child, level + 1);
}
} else {
String key = node.getClassName().toString() + "@" + level;
FeatureValuePair value;
int size = 0;
CharSequence text = node.getText();
if (text != null) {
size = text.toString().length();
}
if (node.getClassName().toString().equals(ImageView.class.getCanonicalName())) {
Rect bounds = new Rect();
node.getBoundsInScreen(bounds);
String timestamp = new SimpleDateFormat("MM-dd'T'HH-mm-ss-SSS").format(new Date());
size = get_imageview_size("/data/local/tmp/local/tmp/haos.png", bounds, "/data/local/tmp/local/tmp/" + key + "_" + timestamp + ".png");
} else {
// Util.log(node.getClassName() + " NOT " + ImageView.class.getCanonicalName());
}
value = map.containsKey(key) ? map.get(key) : new FeatureValuePair(0, 0);
value.count++;
value.size += size;
map.put(key, value);
// Util.log("Leaf: " + key + ", " + size);
}
}
/**
* extractNode.
* @param aAccessibilityEvent aAccessibilityEvent
* @param aNodeClassName aNodeClassName
* @param aNodeText aNodeText
* @return AccessibilityNodeInfo
*/
private AccessibilityNodeInfo extractNode(AccessibilityEvent aAccessibilityEvent, String aNodeClassName, String aNodeText) {
List<AccessibilityNodeInfo> extractList = null;
AccessibilityNodeInfo targetNode = null;
AccessibilityNodeInfo rootNode;
if((aAccessibilityEvent == null) || (aAccessibilityEvent.getSource() == null)) {
rootNode = this.getRootInActiveWindow();
if(rootNode != null) {
extractList = rootNode.findAccessibilityNodeInfosByText(aNodeText);
}
} else {
extractList = aAccessibilityEvent.getSource().findAccessibilityNodeInfosByText(aNodeText);
}
if ((extractList == null) || extractList.isEmpty()) {
targetNode = null;
} else {
Iterator<AccessibilityNodeInfo> it = extractList.iterator();
AccessibilityNodeInfo tempNode = null;
while (it.hasNext()) {
tempNode = it.next();
if (!tempNode.getClassName().equals(aNodeClassName)) {
continue;
}
String nodeName = (tempNode.getText() == null) ? "" : tempNode.getText().toString();
if (!nodeName.endsWith(aNodeText)) {
continue;
}
targetNode = tempNode;
break;
}
}
return targetNode;
}
/**
* Clears the existing text contents in an editable field.
*
* The {@link UiSelector} of this object must reference a UI element that is editable.
*
* When you call this method, the method sets focus on the editable field, selects all of its
* existing content, and clears it by sending a DELETE key press
*
* @throws UiObjectNotFoundException
* @since API Level 16
*/
public void clearTextField() throws UiObjectNotFoundException {
Tracer.trace();
// long click left + center
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
if(node == null) {
throw new UiObjectNotFoundException(mUiSelector.toString());
}
CharSequence text = node.getText();
// do nothing if already empty
if (text != null && text.length() > 0) {
if (UiDevice.API_LEVEL_ACTUAL > Build.VERSION_CODES.KITKAT) {
setText("");
} else {
Bundle selectionArgs = new Bundle();
// select all of the existing text
selectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
selectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
text.length());
boolean ret = node.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
if (!ret) {
Log.w(LOG_TAG, "ACTION_FOCUS on text field failed.");
}
ret = node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, selectionArgs);
if (!ret) {
Log.w(LOG_TAG, "ACTION_SET_SELECTION on text field failed.");
}
// now delete all
getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
}
}
}
/**
* Set the text content by sending individual key codes.
*
* @hide
*/
public void legacySetText(String text) {
AccessibilityNodeInfo node = getAccessibilityNodeInfo();
// Per framework convention, setText(null) means clearing it
if (text == null) {
text = "";
}
CharSequence currentText = node.getText();
if (!text.equals(currentText)) {
InteractionController ic = mDevice.getAutomatorBridge()
.getInteractionController();
// Long click left + center
Rect rect = getVisibleBounds();
ic.longTapNoSync(rect.left + 20, rect.centerY());
// Select existing text
mDevice.wait(Until.findObject(By.descContains("Select all")), 50)
.click();
// Wait for the selection
SystemClock.sleep(250);
// Delete it
ic.sendKey(KeyEvent.KEYCODE_DEL, 0);
// Send new text
ic.sendText(text);
}
}
/**
*
* @param nodeInfo
* @return
*/
public static boolean isIncomingMessage(AccessibilityNodeInfo nodeInfo, Context context) {
Rect rect = new Rect();
nodeInfo.getBoundsInScreen(rect);
return rect.left < UIUtils.getScreenHeight(context)
- rect.right && nodeInfo.getText() != null;
}
public static SerializedAccessibilityNodeInfo serialize(AccessibilityNodeInfo node){
SerializedAccessibilityNodeInfo serializedNode = new SerializedAccessibilityNodeInfo();
Rect boundsInScreen = new Rect(), boundsInParent = new Rect();
if(node == null){
return null;
}
if(node.getClassName() != null)
serializedNode.className = node.getClassName().toString();
node.getBoundsInScreen(boundsInScreen);
node.getBoundsInParent(boundsInParent);
serializedNode.boundsInScreen = boundsInScreen.flattenToString();
serializedNode.boundsInParent = boundsInParent.flattenToString();
if(node.getContentDescription() != null)
serializedNode.contentDescription = node.getContentDescription().toString();
if(node.getText() != null){
serializedNode.text = node.getText().toString();
}
if(node.getViewIdResourceName() != null)
serializedNode.viewId = node.getViewIdResourceName();
int childCount = node.getChildCount();
for(int i = 0; i < childCount; i ++){
if(node.getChild(i) != null){
serializedNode.children.add(serialize(node.getChild(i)));
}
}
return serializedNode;
}
private void getAllHongBao(AccessibilityNodeInfo info)
{
runState = true;
//Log.i(TAG, "获取所有红包");
time = System.currentTimeMillis();
List<AccessibilityNodeInfo> list=new ArrayList<AccessibilityNodeInfo>();
//查找出当前页面所有的红包,包括手气红包和口令红包
for (String word:QQ_KEYWORD_HONGBAO)
{
List<AccessibilityNodeInfo> infolist = info.findAccessibilityNodeInfosByText(word);
if (!infolist.isEmpty())
{
for (AccessibilityNodeInfo node:infolist)
{
//这里进行过滤可点击的红包,放到后面去过滤的话感觉非常操蛋
if (node.getText() == null ||!node.getText().toString().equals(word) ||node.getParent().getChildCount() != 3 ||!node.getParent().findAccessibilityNodeInfosByText(QQ_KEYWORD_FAILD_CLICK).isEmpty())
continue;
list.add(node);
}
}
}
if (list.size() == 0)
{
runState = false;
return ;
}
ToastUtil.show(this,list.size()+"/"+(System.currentTimeMillis() - time) + "", Toast.LENGTH_SHORT);
//Log.i(TAG, "数量>>>" + list.size() + " 获取红包耗时:" + (System.currentTimeMillis() - time) + "ms");
clickAction(list);
}
private void findChildView2(AccessibilityNodeInfo info, String parentText) {
parentText = parentText + " |-- " + info.getText();
L.d("得到控件 " + parentText);
for (int i = 0; i < info.getChildCount(); i++) {
AccessibilityNodeInfo child = info.getChild(i);
if (child != null) {
// L.d("得到子控件 " + child.getText());
findChildView2(child, parentText + "");
} else {
// L.d("得到所有控件" + info.getText());
}
}
}
private synchronized void getText(AccessibilityEvent event) {
LogUtil.d(TAG, "getText:" + event);
if (!monitorClick || event == null) {
return;
}
if (showFloatView && !isRun) {
return;
}
int type = getClickType(event);
CharSequence className = event.getClassName();
if (mWindowClassName == null) {
return;
}
if (mWindowClassName.toString().startsWith("com.time.timecat")) {
//自己的应用不监控
return;
}
if (mCurrentPackage.equals(event.getPackageName())) {
if (type != mCurrentType) {
//点击方式不匹配,直接返回
return;
}
} else {
//包名不匹配,直接返回
return;
}
if (className == null || className.equals("android.widget.EditText")) {
//输入框不监控
return;
}
if (onlyText) {
//onlyText方式下,只获取TextView的内容
if (!className.equals("android.widget.TextView")) {
if (!hasShowTipToast) {
ToastUtil.i(R.string.toast_tip_content);
hasShowTipToast = true;
}
return;
}
}
AccessibilityNodeInfo info = event.getSource();
if (info == null) {
return;
}
CharSequence txt = info.getText();
if (TextUtils.isEmpty(txt) && !onlyText) {
//非onlyText方式下获取文字更多,但是可能并不是想要的文字
//比如系统短信页面需要这样才能获取到内容。
List<CharSequence> txts = event.getText();
if (txts != null) {
StringBuilder sb = new StringBuilder();
for (CharSequence t : txts) {
sb.append(t);
}
txt = sb.toString();
}
}
if (!TextUtils.isEmpty(txt)) {
if (txt.length() <= 2) {
//对于太短的词进行屏蔽,因为这些词往往是“发送”等功能按钮,其实应该根据不同的activity进行区分
if (!hasShowTooShortToast) {
ToastUtil.w(R.string.too_short_to_split);
hasShowTooShortToast = true;
}
return;
}
Intent intent = new Intent(this, TimeCatActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(TimeCatActivity.TO_SPLIT_STR, txt.toString());
// startActivity(intent);
//放到ArcTipViewController中触发试试
ArcTipViewController.getInstance().showTipViewForStartActivity(intent);
}
}
/**
* Processes an AccessibilityEvent, by traversing the View's tree and
* putting together a message to speak to the user.
*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (!mTextToSpeechInitialized) {
Log.e(LOG_TAG, "Text-To-Speech engine not ready. Bailing out.");
return;
}
// This AccessibilityNodeInfo represents the view that fired the
// AccessibilityEvent. The following code will use it to traverse the
// view hierarchy, using this node as a starting point.
//
// NOTE: Every method that returns an AccessibilityNodeInfo may return null,
// because the explored window is in another process and the
// corresponding View might be gone by the time your request reaches the
// view hierarchy.
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
// Grab the parent of the view that fired the event.
AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
if (rowNode == null) {
return;
}
// Using this parent, get references to both child nodes, the label and the checkbox.
AccessibilityNodeInfo labelNode = rowNode.getChild(0);
if (labelNode == null) {
rowNode.recycle();
return;
}
AccessibilityNodeInfo completeNode = rowNode.getChild(1);
if (completeNode == null) {
rowNode.recycle();
return;
}
// Determine what the task is and whether or not it's complete, based on
// the text inside the label, and the state of the check-box.
if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {
rowNode.recycle();
return;
}
CharSequence taskLabel = labelNode.getText();
final boolean isComplete = completeNode.isChecked();
String completeStr = null;
if (isComplete) {
completeStr = getString(R.string.task_complete);
} else {
completeStr = getString(R.string.task_not_complete);
}
String taskStr = getString(R.string.task_complete_template, taskLabel, completeStr);
StringBuilder utterance = new StringBuilder(taskStr);
// The custom ListView added extra context to the event by adding an
// AccessibilityRecord to it. Extract that from the event and read it.
final int records = event.getRecordCount();
for (int i = 0; i < records; i++) {
AccessibilityRecord record = event.getRecord(i);
CharSequence contentDescription = record.getContentDescription();
if (!TextUtils.isEmpty(contentDescription )) {
utterance.append(SEPARATOR);
utterance.append(contentDescription);
}
}
// Announce the utterance.
mTts.speak(utterance.toString(), TextToSpeech.QUEUE_FLUSH, null);
Log.d(LOG_TAG, utterance.toString());
}
protected void parser(AccessibilityNodeInfo group) {
if (group.getChildCount() == 0)
return;
int headIconPos = 0;
int messagePos = 0;
subName = "";
message = "";
isRecalledMsg = false;
for (int j = 0; j < group.getChildCount(); j++) {
AccessibilityNodeInfo child = group.getChild(j);
if (child == null) {
Log.d(TAG, "parser: child is null, continue");
continue;
}
String nodeId = child.getViewIdResourceName();
if (nodeId == null) {
Log.d(TAG, "parser: node ID is null, continue");
continue;
}
switch (nodeId) {
case IdHeadIcon:
//头像图标
headIconPos = j;
break;
case IdChatItem:
switch (child.getClassName() + "") {
case "android.widget.RelativeLayout":
if (child.getChildCount() != 0) {
if (child.getContentDescription() != null) {
redPegNode = child;
message = "红包";
//红包或者是分享
Log.d(TAG, "content_layout: 红包");
}
} else {
message = "[图片]";
Log.d(TAG, "content_layout: 图片");
}
break;
case "android.widget.LinearLayout": {
if (child.getChildCount() == 2) {
AccessibilityNodeInfo child1 = child.getChild(0);
AccessibilityNodeInfo child2 = child.getChild(1);
if (child1 != null && "android.widget.RelativeLayout".contentEquals(child1.getClassName())) {
if (child2 != null && "android.widget.TextView".contentEquals(child2.getClassName())) {
// message = "回复 " + child1.getText() + ": \n" + child2.getText();
message = child2.getText() + "";
}
}
}
Log.d(TAG, "content_layout: 回复消息");
}
break;
case "android.widget.TextView": {
message = child.getText() + "";
Log.v(TAG, "content_layout: 普通文本");
}
break;
}
messagePos = j;
break;
case IdNickName:
subName = child.getText() + "";
break;
case IdGrayBar:
message = child.getText() + "";
Log.w(TAG, "parser: message: " + message);
int indexOfRecall = message.indexOf(RECALL);
if (indexOfRecall >= 0) {
isRecalledMsg = true;
subName = message.substring(0, indexOfRecall);
message = RECALL;
if ("对方".equals(subName))
subName = title;
else if ("你".equals(subName))
subName = "我";
Log.v(TAG, "content_layout: 灰底文本");
}
Log.w(TAG, "parser: " + indexOfRecall + " " + RECALL + " " + message);
break;
}
}
if (messagePos < headIconPos)
// 消息在头像左边
subName = "我";
else if ("".equals(subName))
// 两人聊天时 没有subName
subName = title;
Log.v(TAG, "parser: " + title + " - " + subName + " : " + message);
}