Android无法在少数使用辅助功能服务的设备上读取窗口内容
我的要求:阅读文本从弹出窗口,对话框等特定的应用程序 我已经实施了无障碍服务,并根据我的要求接收适当的事件和数据。然而,在测试过程中,我意识到在一些设备上,他们没有使用AlertDialog或Dialog,而是使用了活动(主题为对话框)。 因此,在我的可访问性事件中,我只收到活动标题,是否有办法找到此特定弹出式活动显示的文本 我搜索得很辛苦,但在这个主题上没有得到太多的帮助,文档在这个问题上也没有任何用处。无障碍服务代码中没有太多内容,但如果您仍然需要,我将稍后发布Android无法在少数使用辅助功能服务的设备上读取窗口内容,android,accessibilityservice,accessibility-api,Android,Accessibilityservice,Accessibility Api,我的要求:阅读文本从弹出窗口,对话框等特定的应用程序 我已经实施了无障碍服务,并根据我的要求接收适当的事件和数据。然而,在测试过程中,我意识到在一些设备上,他们没有使用AlertDialog或Dialog,而是使用了活动(主题为对话框)。 因此,在我的可访问性事件中,我只收到活动标题,是否有办法找到此特定弹出式活动显示的文本 我搜索得很辛苦,但在这个主题上没有得到太多的帮助,文档在这个问题上也没有任何用处。无障碍服务代码中没有太多内容,但如果您仍然需要,我将稍后发布 谢谢这是我使用的代码,适用于
谢谢这是我使用的代码,适用于我:
public class USSDService extends AccessibilityService {
public static String TAG = USSDService.class.getSimpleName();
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.d(TAG, "onAccessibilityEvent");
AccessibilityNodeInfo source = event.getSource();
if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !String.valueOf(event.getClassName()).contains("AlertDialog")) {
return;
}
if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && (source == null || !source.getClassName().equals("android.widget.TextView"))) {
return;
}
if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && TextUtils.isEmpty(source.getText())) {
return;
}
List<CharSequence> eventText;
if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
eventText = event.getText();
} else {
eventText = Collections.singletonList(source.getText());
}
String text = processUSSDText(eventText);
if( TextUtils.isEmpty(text) ) return;
// Close dialog
performGlobalAction(GLOBAL_ACTION_BACK); // This works on 4.1+ only
Log.d(TAG, text);
// Handle USSD response here
}
private String processUSSDText(List<CharSequence> eventText) {
for (CharSequence s : eventText) {
String text = String.valueOf(s);
// Return text if text is the expected ussd response
if( true ) {
return text;
}
}
return null;
}
@Override
public void onInterrupt() {
}
@Override
protected void onServiceConnected() {
super.onServiceConnected();
Log.d(TAG, "onServiceConnected");
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.flags = AccessibilityServiceInfo.DEFAULT;
info.packageNames = new String[]{"com.android.phone"};
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
setServiceInfo(info);
}
}
公共类USSDService扩展了AccessibilityService{
公共静态字符串标记=USSDService.class.getSimpleName();
@凌驾
AccessibilityEvent上的公共无效(AccessibilityEvent事件){
Log.d(标记“onAccessibilityEvent”);
AccessibilityNodeInfo source=event.getSource();
如果(event.getEventType()==AccessibilityEvent.TYPE\u WINDOW\u STATE\u已更改&&!String.valueOf(event.getClassName()).contains(“AlertDialog”)){
返回;
}
如果(event.getEventType()==AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED&&(source==null | | |!source.getClassName().equals(“android.widget.TextView”)){
返回;
}
if(event.getEventType()==AccessibilityEvent.TYPE\u WINDOW\u CONTENT\u CHANGED&&TextUtils.isEmpty(source.getText()){
返回;
}
列出事件文本;
if(event.getEventType()==AccessibilityEvent.TYPE\u窗口\u状态\u已更改){
eventText=event.getText();
}否则{
eventText=Collections.singletonList(source.getText());
}
String text=processUSSDText(eventText);
if(TextUtils.isEmpty(text))返回;
//关闭对话框
performGlobalAction(GLOBAL_ACTION_BACK);//这仅适用于4.1+版本
Log.d(标签、文本);
//在这里处理USSD响应
}
私有字符串processUSSDText(列表事件文本){
for(字符序列:eventText){
String text=String.valueOf(s);
//如果文本是预期的ussd响应,则返回文本
如果(真){
返回文本;
}
}
返回null;
}
@凌驾
在中断时的公共无效(){
}
@凌驾
ServiceConnected()上受保护的void{
super.onServiceConnected();
Log.d(标记“onServiceConnected”);
AccessibilityServiceInfo=新的AccessibilityServiceInfo();
info.flags=AccessibilityServiceInfo.DEFAULT;
info.packageNames=新字符串[]{“com.android.phone”};
info.eventTypes=AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
info.feedbackType=可访问性服务info.FEEDBACK\u通用;
设置服务信息(信息);
}
}
在Android清单中声明它
<service android:name=".USSDService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="@xml/ussd_service" />
创建一个xml文件,该文件描述称为ussd_服务的辅助功能服务
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="@string/accessibility_service_description"
android:notificationTimeout="0"
android:packageNames="com.android.phone" />
使用accessiblityNodeInfo获取信息,即使在手机返回的情况下,它也会获取ussd响应,并且当有选项输入多个选项时,它会关闭对话框 首先,在[pohne]事件的情况下,类名称作为ussdalertactivity返回,因此我仅使用“alert”来标识ussd响应的警报对话框
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getPackageName().toString().equals("com.android.phone")
&& event.getClassName().toString().toLowerCase()
.contains("alert")) {
AccessibilityNodeInfo source = event.getSource();
if (source != null) {
String pcnResponse = fetchResponse(source);
}
}
现在我创建了一个名为fetchResponse的函数,它将以字符串形式返回来自pcn的响应,并且还将关闭对话框,因此需要执行performGlobalAction(全局动作返回)
私有字符串fetchResponse(AccessibilityNodeInfo AccessibilityNodeInfo){
字符串fetchedResponse=“”;
if(accessibilityNodeInfo!=null){
对于(int i=0;i10){
fetchedResponse=text.toString();
}
}else如果(child.getClassName().equals(
ScrollView.class.getName()){
//当响应以电话的形式出现时,响应可以是
//从亚孩子那里得到的
对于(int j=0;j10){
fetchedResponse=subText.toString();
}
private String fetchResponse(AccessibilityNodeInfo accessibilityNodeInfo) {
String fetchedResponse = "";
if (accessibilityNodeInfo != null) {
for (int i = 0; i < accessibilityNodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo child = accessibilityNodeInfo.getChild(i);
if (child != null) {
CharSequence text = child.getText();
if (text != null
&& child.getClassName().equals(
Button.class.getName())) {
// dismiss dialog by performing action click in normal
// cases
if((text.toString().toLowerCase().equals("ok") || text
.toString().toLowerCase()
.equals("cancel"))) {
child.performAction(AccessibilityNodeInfo.ACTION_CLICK);
return fetchedResponse;
}
} else if (text != null
&& child.getClassName().equals(
TextView.class.getName())) {
// response of normal cases
if (text.toString().length() > 10) {
fetchedResponse = text.toString();
}
} else if (child.getClassName().equals(
ScrollView.class.getName())) {
// when response comes as phone then response can be
// retrived from subchild
for (int j = 0; j < child.getChildCount(); j++) {
AccessibilityNodeInfo subChild = child.getChild(j);
CharSequence subText = subChild.getText();
if (subText != null
&& subChild.getClassName().equals(
TextView.class.getName())) {
// response of different cases
if (subText.toString().length() > 10) {
fetchedResponse = subText.toString();
}
}
else if (subText != null
&& subChild.getClassName().equals(
Button.class.getName())) {
// dismiss dialog by performing action click in
// different
// cases
if ((subText.toString().toLowerCase()
.equals("ok") || subText
.toString().toLowerCase()
.equals("cancel"))) {
subChild.performAction(AccessibilityNodeInfo.ACTION_CLICK);
return fetchedResponse;
}
}
}
}
}
}
}
return fetchedResponse;
}
private void clickPerform(AccessibilityNodeInfo nodeInfo)
{
if(nodeInfo != null)
{
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo childNode = nodeInfo.getChild(i);
Log.e("test", "clickPerform: "+childNode );
if (childNode != null) {
for (int j = 0; j <= childNode.getChildCount(); j++) {
AccessibilityNodeInfo subChild = childNode.getChild(i);
if (String.valueOf(subChild.getText()).toLowerCase().equals("ok")) {
subChild.performAction(AccessibilityNodeInfo.ACTION_CLICK);
} else {
Log.e("t2", "clickPerform: ");
}
}
}
}
}
}