7

我的要求:从特定应用程序的弹出窗口、对话框等中读取文本。

我已经实施了无障碍服务,并且我正在根据我的要求接收适当的事件和数据。然而,在测试时,我意识到在某些设备上,他们没有使用 AlertDialog 或 Dialog,而是使用了一个活动(主题为对话框)。因此,在我的可访问性事件中,我只收到活动标题,有没有办法可以找到这个特定弹出活动显示的文本?

我已经进行了相当艰苦的搜索,但在该主题上没有得到太多帮助,文档也没有在这个问题上有任何好处。无障碍服务的代码不多,但如果您仍然需要,我会稍后发布。

谢谢

4

3 回答 3

5

Use accessiblityNodeInfo to get information even in the case of when phone is returning it will fetch the ussd response also it will dismiss dialog when there is option to enter multiple choices.

First of all in case of [pohne] event class name is returned as ussdalertactivty so i used only "alert" for identification of alert dialog of ussd response.

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);
                    }
    }

Now i have made a function called fetchResponse which will return the response from pcn as string and will also dismiss the dialog so need to do performGlobalAction(GLOBAL_ACTION_BACK).

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;
    }

Hope this will solve the issue irrespective of the devices.

于 2016-04-26T10:02:41.087 回答
3

这是我使用的代码,它适用于我:

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);
}
}

在 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_service

<?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" />
于 2015-12-21T16:56:35.077 回答
1

您应该检查子节点是否有任何子节点。

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: ");
                        }
                    }
                }

                }
            }
}
于 2018-05-17T05:20:54.647 回答