如何使用辅助功能从浏览器获取URL

问题描述

我已启用了无障碍服务权限,现在我想从地址栏中获取网址。

我尝试了以下方法

accessibility_service_config.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" />

AccessService.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。我也想在地址栏中的facebook.com时打开新标签关闭现有标签

有什么合适的方法吗?

解决方法

我要在此处添加一些内容,以及我们如何改进您的解决方案。如果您接受以下限制可能会更好:

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

我只想在地址栏中将其击中后获取URL。

  • 最后一个限制。尽管我们能够能够拦截 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" />

随时问任何问题,我会尽力帮助

,

首先,在accessibility_service XML中添加标志和包

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews|flagRequestTouchExplorationMode|flagRequestEnhancedWebAccessibility|flagReportViewIds|flagRetrieveInteractiveWindows"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_description"
    android:notificationTimeout="0"
    android:canRequestFilterKeyEvents="true"
    android:packageNames="com.android.chrome"
    android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity" />

在AndroidManifest中:

<service android:name=".MyAccessibilityService"
        android:label="@string/accessibility_title"
        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/accessibility_service" />
    </service>

java类:

public class ASUrl extends AccessibilityService {

      @Override
      public void onAccessibilityEvent(AccessibilityEvent event) {
  if(AccessibilityEvent.eventTypeToString(event.getEventType()).contains("WINDOW")){
         AccessibilityNodeInfo nodeInfo = event.getSource();
         dfs(nodeInfo);
    }
      }

      /**
      * Method to loop through all the views and try to find a URL.
      * @param info
      */
      public void getUrlsFromViews(AccessibilityNodeInfo info) {  
        if(info == null)
          return;

        if(info.getText() != null && info.getText().length() > 0)
          System.out.println(info.getText() + " class: "+info.getClassName());

        for(int i=0;i<info.getChildCount();i++){
         AccessibilityNodeInfo child = info.getChild(i);
         getUrlsFromViews(child);
          if(child != null){
            child.recycle();
          }
        }
      }
 
  }

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...