android - 如何使用无障碍服务从浏览器获取 url

标签 android accessibilityservice accessibility

我启用了可访问性服务权限,现在我想从地址栏中获取 url。
我尝试过以下事情:
无障碍服务配置.xml

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_description"
    android:notificationTimeout="0"
    android:canRequestFilterKeyEvents="true"
    android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity" />
访问服务.java
public class AccessService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        AccessibilityNodeInfo source = event.getSource();
        if (source == null)
            return;
        final String packageName = String.valueOf(source.getPackageName());
        String BROWSER_LIST = "com.android.chrome";
        List<String> browserList
                = Arrays.asList(BROWSER_LIST.split(",\\s*"));
        if (event.getEventType()
                == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
            if (!browserList.contains(packageName)) {
                return;
            }
        }

        if (browserList.contains(packageName)) {
            try {
                if (AccessibilityEvent
                        .eventTypeToString(event.getEventType())
                        .contains("WINDOW")) {
                    AccessibilityNodeInfo nodeInfo = event.getSource();
                    getUrlsFromViews(nodeInfo);
                }
            } catch (StackOverflowError ex) {
                ex.printStackTrace();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public void getUrlsFromViews(AccessibilityNodeInfo info) {

        try {
            if (info == null)
                return;
            if (info.getText() != null && info.getText().length() > 0) {
                String capturedText = info.getText().toString();
                Bundle arguments = new Bundle();
                if (capturedText.contains("https://")
                        || capturedText.contains("http://")) {

                   if (capturedText.contains("facebook.com")) {
                     // open new tab
                  }
                }
            }
            for (int i = 0; i < info.getChildCount(); i++) {
                AccessibilityNodeInfo child = info.getChild(i);
                getUrlsFromViews(child);
                if (child != null) {
                    child.recycle();
                }
            }
        } catch (StackOverflowError ex) {
            ex.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void onInterrupt() {

    }

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
    }
}
我在这里面临的问题是,当我在地址栏中键入 facebook.com 并点击 url 时,我只得到 facebook.comm.facebook.com因此,我无法采取任何行动。
我只想在地址栏中点击它后获取 URL。我也想在地址栏中打开新标签并关闭现有标签。
有没有合适的方法来做到这一点?

最佳答案

我有一些要补充的内容以及我们如何改进您的解决方案。如果您接受以下限制可能会更好:

  • 该应用程序具有支持的浏览器的嵌入式列表。不支持任何其他浏览器。
  • 无障碍服务将寻找 编号 地址栏文本字段并尝试从那里截取 URL。无法直接捕获要加载的 URL。为了找到这个 id,我们应该对目标浏览器进行一些逆向工程:通过无障碍服务收集所有字段,并将它们的 id 和值与用户输入进行比较。
  • 从上一点来看,下一个限制是我们将只支持当前版本的浏览器。第三方浏览器开发人员将来可能会更改 id,我们将不得不更新我们的拦截器以继续支持。这可以通过更新应用程序或通过远程服务器提供浏览器包到 id 映射来完成
  • 我们检测到手动用户输入或在链接按下时重定向(因为在这种情况下,新 URL 也将在地址栏中可见)。顺便说一句,不清楚您所说的
  • 是什么意思

    I want to get the URL only after it is hit in the address bar.


  • 最后一个限制。尽管 我们能够拦截 URL 和 重定向 用户到另一个页面,由于异步解析浏览器应用程序屏幕的延迟,我们无法阻止该站点被加载或开始加载。例如。保护用户免受对欺诈网站的任何访问并不是真正安全的

  • 实现:
        public class UrlInterceptorService extends AccessibilityService {
            private HashMap<String, Long> previousUrlDetections = new HashMap<>();
    
            @Override
            protected void onServiceConnected() {
                AccessibilityServiceInfo info = getServiceInfo();
                info.eventTypes = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
                info.packageNames = packageNames();
                info.feedbackType = AccessibilityServiceInfo.FEEDBACK_VISUAL;
                //throttling of accessibility event notification
                info.notificationTimeout = 300;
                //support ids interception
                info.flags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS |
                        AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
    
                this.setServiceInfo(info);
            }
    
            private String captureUrl(AccessibilityNodeInfo info, SupportedBrowserConfig config) {
                List<AccessibilityNodeInfo> nodes = info.findAccessibilityNodeInfosByViewId(config.addressBarId);
                if (nodes == null || nodes.size() <= 0) {
                    return null;
                }
    
                AccessibilityNodeInfo addressBarNodeInfo = nodes.get(0);
                String url = null;
                if (addressBarNodeInfo.getText() != null) {
                    url = addressBarNodeInfo.getText().toString();
                }
                addressBarNodeInfo.recycle();
                return url;
            }
    
            @Override
            public void onAccessibilityEvent(@NonNull AccessibilityEvent event) {
                AccessibilityNodeInfo parentNodeInfo = event.getSource();
                if (parentNodeInfo == null) {
                    return;
                }
    
                String packageName = event.getPackageName().toString();
                SupportedBrowserConfig browserConfig = null;
                for (SupportedBrowserConfig supportedConfig: getSupportedBrowsers()) {
                    if (supportedConfig.packageName.equals(packageName)) {
                        browserConfig = supportedConfig;
                    }
                }
                //this is not supported browser, so exit
                if (browserConfig == null) {
                    return;
                }
    
                String capturedUrl = captureUrl(parentNodeInfo, browserConfig);
                parentNodeInfo.recycle();
    
                //we can't find a url. Browser either was updated or opened page without url text field
                if (capturedUrl == null) {
                    return;
                }
    
                long eventTime = event.getEventTime();
                String detectionId = packageName + ", and url " + capturedUrl;
                //noinspection ConstantConditions
                long lastRecordedTime = previousUrlDetections.containsKey(detectionId) ? previousUrlDetections.get(detectionId) : 0;
                //some kind of redirect throttling
                if (eventTime - lastRecordedTime > 2000) {
                    previousUrlDetections.put(detectionId, eventTime);
                    analyzeCapturedUrl(capturedUrl, browserConfig.packageName);
                }
            }
    
            private void analyzeCapturedUrl(@NonNull String capturedUrl, @NonNull String browserPackage) {
                String redirectUrl = "your redirect url is here";
                if (capturedUrl.contains("facebook.com")) {
                    performRedirect(redirectUrl, browserPackage);
                }
            }
    
            /** we just reopen the browser app with our redirect url using service context
             * We may use more complicated solution with invisible activity to send a simple intent to open the url */
            private void performRedirect(@NonNull String redirectUrl, @NonNull String browserPackage) {
                try {
                    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl));
                    intent.setPackage(browserPackage);
                    intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackage);
                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                    startActivity(intent);
                }
                catch(ActivityNotFoundException e) {
                    // the expected browser is not installed
                    Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl));
                    startActivity(i);
                }
            }
    
            @Override
            public void onInterrupt() { }
    
            @NonNull
            private static String[] packageNames() {
                List<String> packageNames = new ArrayList<>();
                for (SupportedBrowserConfig config: getSupportedBrowsers()) {
                    packageNames.add(config.packageName);
                }
                return packageNames.toArray(new String[0]);
            }
    
            private static class SupportedBrowserConfig {
                public String packageName, addressBarId;
                public SupportedBrowserConfig(String packageName, String addressBarId) {
                    this.packageName = packageName;
                    this.addressBarId = addressBarId;
                }
            }
    
            /** @return a list of supported browser configs
             * This list could be instead obtained from remote server to support future browser updates without updating an app */
            @NonNull
            private static List<SupportedBrowserConfig> getSupportedBrowsers() {
                List<SupportedBrowserConfig> browsers = new ArrayList<>();
                browsers.add( new SupportedBrowserConfig("com.android.chrome", "com.android.chrome:id/url_bar"));
                browsers.add( new SupportedBrowserConfig("org.mozilla.firefox", "org.mozilla.firefox:id/url_bar_title"));
                return browsers;
            }
        }
    
    和无障碍服务配置:
    <accessibility-service
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:description="@string/accessibility_service_description"
        android:canRetrieveWindowContent="true"
        android:settingsActivity=".ServiceSettingsActivity" />
    
    随时提出任何问题,我会尽力提供帮助

    关于android - 如何使用无障碍服务从浏览器获取 url,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63880266/

    相关文章:

    android - Kotlin:如何在 RecyclerView.Adapter 中调用 getSupportFragmentManager()

    android - 如果设置较长的文本, TextView 宽度不会改变

    android - 辅助功能服务无法在浏览器窗口中为 EditText 设置文本

    android - 三星设备和辅助功能服务(ACTION_PASTE、剪贴板)

    java - 有没有办法使用 AccessibilityService 单击对话框中的超链接?

    android - 如何在 Android Studio 上冷启动运行 API 27+ 的模拟器?

    android - 这个 React-Native 方法示例中的类 "Promise"应该来自哪里?

    c# - 使屏幕阅读器可以访问 WPF 应用程序

    google-maps - 什么是适合 map 元素的 WAI-ARIA 角色属性

    jquery - 预输入和屏幕阅读器