So erhalten Sie eine URL vom Browser mithilfe des Eingabehilfedienstes
Ich habe die Accessibility-Service-Berechtigung aktiviert und möchte jetzt die URL von der Adressleiste abrufen.
Ich habe unten versucht:
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();
}
}
Das Problem, mit dem ich hier konfrontiert bin, ist, wenn ich facebook.com in die Adressleiste eingebe und die URL drücke, dann erhalte ich nur facebook.comoder m.facebook.comund aus diesem Grund kann ich keine Maßnahmen ergreifen.
Ich möchte die URL erst erhalten, nachdem sie in der Adressleiste angezeigt wurde. Außerdem möchte ich eine neue Registerkarte öffnen und eine vorhandene Registerkarte schließen, wenn facebook.com in der Adressleiste angezeigt wird.
Gibt es einen richtigen Weg, dies zu tun?
Antworten
Ich habe hier etwas hinzuzufügen und wie wir Ihre Lösung verbessern können. Es könnte besser sein, wenn Sie die folgenden Einschränkungen akzeptieren:
- Die Anwendung verfügt über eine eingebettete Liste der unterstützten Browser. Jeder andere Browser wird nicht unterstützt.
- Der Eingabehilfedienst sucht nach einer ID eines Adressfeld- Textfelds und versucht, die URL von dort abzufangen. Es ist nicht möglich, die zu ladende URL direkt abzufangen. Um diese ID zu finden, sollten wir ein wenig Reverse Engineering des Zielbrowsers durchführen: Sammeln Sie alle Felder vom Eingabehilfedienst und vergleichen Sie ihre IDs und Werte mit Benutzereingaben.
- Ab dem vorherigen Punkt besteht die nächste Einschränkung darin, dass wir nur die aktuelle Version des Browsers unterstützen. Die ID könnte in Zukunft von Browser-Entwicklern von Drittanbietern geändert werden, und wir müssen unseren Interceptor aktualisieren, um den Support fortzusetzen. Dies kann entweder durch Aktualisieren einer App oder durch Bereitstellen des Browserpakets für die ID-Zuordnung durch den Remote-Server erfolgen
- Wir erkennen entweder manuelle Benutzereingaben oder Weiterleitungen beim Drücken von Links (da in diesem Fall die neue URL auch in einer Adressleiste angezeigt wird). Übrigens ist nicht klar, was Sie gemeint haben, wenn gesagt
Ich möchte die URL erst erhalten, nachdem sie in der Adressleiste angezeigt wurde.
- Die letzte Einschränkung. Obwohl wir in der Lage sind , die URL abzufangen und den Benutzer auf eine andere Seite umzuleiten , können wir aufgrund von Verzögerungen beim asynchronen Parsen eines Browser-App-Bildschirms nicht verhindern, dass die Site geladen oder gestartet wird. Beispielsweise ist es nicht wirklich sicher, einen Benutzer vor dem Zugriff auf eine Betrugsseite zu schützen
Die Umsetzung:
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;
}
}
und Accessibility Service-Konfiguration:
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description"
android:canRetrieveWindowContent="true"
android:settingsActivity=".ServiceSettingsActivity" />
Fühlen Sie sich frei, Fragen zu stellen und ich werde versuchen zu helfen
Fügen Sie zunächst Flag und Paket in accessibility_service XML hinzu
<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" />
in 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>
Klasse 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();
}
}
}
}