it-swarm.com.de

Wie kann ich den Fensterinhalt (mithilfe von accessibilityService) lesen und die Benutzeroberfläche mithilfe von Draw über andere App-Berechtigungen in Android ziehen?

Meine letzte Frage zu demselben Thema war nicht klar genug und wurde von der Community angehalten und später automatisch gelöscht. Ich erkläre diese Frage also ausführlich, damit die Gemeinschaft besser verstehen und helfen kann.

Ich möchte ähnliche Funktionen wie Voodoo App und MySmartPrice .

Jetzt was sie tun

Schritt 1: Wenn wir die Voodoo-App zum ersten Mal öffnen, wird ein kleines Tutorial gezeigt. Am Ende des Tutorials befindet sich eine Schaltfläche "Jetzt aktivieren", die uns beim Drücken zum Bildschirm Einstellungen für Barrierefreiheit führt.

Schritt 2: Auf dem Eingabebildschirm wird weiter beschrieben, wie Sie den Voodoo-Dienst suchen und nicht finden können.

Schritt 3: Wenn Sie diese Option aktivieren, werden Sie weiterhin gebeten, die Berechtigungen "Beobachten Sie Ihre Aktion" und "Fensterinhalt abrufen" zu erteilen.

Schritt 4: Wenn Sie mit der Erteilung von Berechtigungen auf dem Bildschirm für die Zugänglichkeit fertig sind, wechseln Sie zu einer Einkaufs-App oder rufen Sie die Shopping-Site über den Browser auf und erreichen die Produktseite. Eine Voodoo-Schaltfläche erscheint automatisch.

Schritt 5: Wenn wir darauf klicken, wird der Preis der gleichen/verwandten/ähnlichen Produkte angezeigt, die in anderen Apps oder Websites verfügbar sind, sodass der Benutzer nach dem besten verfügbaren Angebot suchen kann.

Referenz-Screenshot von Voodoo

 Voodoo Screen 1

 Accessibility screen

 Accessibility screen 2

 Permission Granting Screen

 Voodoo Screen 2

 Voodoo Floating Button on product page

 Voodoo Results with the help of draw over other app

Nun, was ich von der Community wissen möchte:

  1. Wie kann ich meine Hilfe-Benutzeroberfläche über den Bildschirm "Eingabehilfen" und die Produktdetailseite anzeigen?.
  2. Wie kann ich feststellen, wann eine Produktseite auf dem Bildschirm angezeigt wird, und wie ich meine Floating-Schaltfläche anrufe.
  3. So rufen Sie den auf dem Bildschirm angezeigten Produktnamen ab.
  4. Wie beschränken Sie diese Bildschirmlesefunktion nur für einige Apps? (da ich nicht in einem urheberrechtlichen Problem enden möchte)
  5. Gibt es ein Tutorial, das mir helfen kann? Ich hatte zwar schon bei Google für jedes direkte Tutorial versucht und hatte keinen Erfolg. 

Warum brauche ich diese Informationen?

Ich plane eine App für Studenten, die ihnen hilft, das gewünschte (oder ähnliche) Buch (E-Book) kostenlos zu erhalten, wenn es auf meinem Server verfügbar ist oder an einem anderen Web-Standort, der im Web verfügbar ist. Ich möchte die Funktionalität auf einige Apps nur aufgrund von Copyright-Problemen in einigen Büchern beschränken.

Nun, was ich aus meiner Studie zu diesem Thema weiß (aber ich bin mir nicht sicher, ob ich auf dem richtigen Weg bin oder nicht)

17
Umesh Chauhan

Ich hatte versucht zusammenzufassen, wie ich das Problem in einem Blogbeitrag durchstehen konnte. Wer Hilfe braucht, kann einen Blick darauf werfen.

Accessibility Service auf die nächste Ebene

Meine vorherige Antwort wurde von Moderatoren möglicherweise gelöscht, da ich gerade einen Link zum Blog gepostet habe. Deshalb werde ich auch hier meine Antwort mit Details veröffentlichen.

Der einfachste Weg bietet Android selbst. Wenn Sie etwas auf Ihrer eigenen App erstellen, ist dies die beste und einfachste Möglichkeit, dies zu tun. Sie müssen "findAccessibilityNodeInfosByViewId ()" Wie unten beschrieben verwenden

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    AccessibilityNodeInfo source = event.getSource(); 
    if (source == null) {
        return; 
    } 
    List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId = source.findAccessibilityNodeInfosByViewId("YOUR PACKAGE NAME:id/RESOURCE ID FROM WHERE YOU WANT DATA"); 
    if (findAccessibilityNodeInfosByViewId.size() > 0) {
        AccessibilityNodeInfo parent = (AccessibilityNodeInfo) findAccessibilityNodeInfosByViewId.get(0);
        // You can also traverse the list if required data is deep in view hierarchy. 
        String requiredText = parent.getText().toString();
        Log.i("Required Text", requiredText);
    }
}  

Wenn Sie etwas in andere Apps integrieren und die Res-ID nicht kennen. Sie müssen eine Logik mit der Kombination aus übergeordnetem Element, untergeordneter Anzahl, Ebene, auf der Ihre Daten gefunden werden, Ereignistyp erstellen. Sie müssen nach einem Muster in Ereignis oder Quelle oder der Kombination der oben genannten Werte suchen, um die erforderlichen Daten wertvoll zu erhalten. Außerdem müssen Sie einige Tweak in Ihrem Code einrichten, um die genauen Daten entsprechend Ihrer Aufgabe zu erhalten. Wie in unserer Aufgabe haben wir einige Regex, um wertvolle Daten zu erhalten.

Sie müssen darauf achten, dass bei einer Änderung der Ansicht oder der Benutzeroberfläche oder bei Änderung der IDs Ihre aktuelle Logik möglicherweise fehlschlägt. Daher müssen Sie die App, auf der Sie Ihren Service aufbauen, genau unter die Lupe nehmen. Es folgen einige Beispielcodes, in denen wir Daten ohne res id abrufen, die für Sie hilfreich sein könnten, um zu verstehen, wie sie funktionieren.

Quelle und Ereignis drucken

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    AccessibilityNodeInfo source = event.getSource();
    if (source == null) {
        return;
    } 
    Log.i("Event", event.toString+””); 
    Log.i("Source", source.toString+””);
}

Um die Anzahl der Kinder zu erhalten

source.getChildCount();

Wenn untergeordnete Anzahl> 0 ist, müssen Sie möglicherweise nachsehen.

Beispielcode 1

if (level == 0 && source.getClassName().equals("Android.widget.TextView") && source.getText()!=null && !source.getText().toString().isEmpty()){
    // here level is iteration of for loop
    String recivedText = source.getText().toString(); 
    if(source.getClassName().equals("Android.widget.TextView") && source.getParent()!=null && source.getParent().getClassName().equals("Android.widget.FrameLayout") && source.getParent().getParent()==null){ 
        return recivedText;
    }
}

Beispielcode 2

if (source.getPackageName().equals("PACKAGE NAME OF APP FOR WHICH YOUR EXPECTING EVENT")) {
    if(event.getEventType()==AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && "COMPLETE NAME OF ACTIVITY CLASS WITH PACKAGE NAME (if you want it for some specific screen)".equals(event.getClassName())){
        if(source.getText()!=null && source.getClassName().equals("Android.widget.TextView") && source.getParent()!=null && source.getParent().getClassName().equals("Android.widget.RelativeLayout")&& source.getParent().getParent()!=null && source.getParent().getParent().getClassName().equals("Android.widget.ScrollView") && source.getParent().getParent().getParent()!=null && source.getParent().getParent().getParent().getClassName().equals("Android.support.v4.view.ViewPager")&& source.getParent().getParent().getParent().getParent()!=null && source.getParent().getParent().getParent().getParent().getClassName().equals("Android.widget.FrameLayout")&& source.getParent().getParent().getParent().getParent().getParent()==null){
        return source.getText().toString();
        }
}

Es ist etwas unbeholfen, aber damit es funktioniert, müssen Sie tief in die Protokolle schauen, um ein Muster zu finden, das Sie zu Ihren erforderlichen Daten führt.

Update 20/11/2017 Google setzt alle Apps aus, die Accessibility Services für andere Zwecke als Accessibility verwenden. Ich hatte eine E-Mail bezüglich einer meiner App erhalten und andere Entwickler erhalten dieselbe E-Mail von Google ( Reddit ). Ich denke, wir sollten nach einem anderen Weg suchen, Dinge zu tun, und ich bitte Sie, es hier ebenfalls zu aktualisieren, falls jemand andere Implementierungen vorstellt, um dasselbe Verhalten zu erreichen, würde dies auch anderen helfen.

Vielen Dank. Umesh Chauhan

24
Umesh Chauhan

Ich habe erfolgreich diese Art von Anwendung wie voodoo gemacht. Die folgende Methode wird verwendet, um den Produktbildschirm des Benutzers anzuzeigen.

private boolean isOpenFlipkartProductScreen(AccessibilityNodeInfo nodeInfo){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        if(nodeInfo.getViewIdResourceName()!=null){
            if(nodeInfo.getViewIdResourceName().equals("com.flipkart.Android:id/callout_last_linearlayout")){
                Utils.sPrint("You just open product page : " + nodeInfo.getClass());
                isOpenProductPage = true;
                return true;
            }
        }
    }
    return false;
}

und folgenden Code, um den Namen und den Preis des Produkttitels zu erhalten.

private String getObtainTitle(AccessibilityNodeInfo sourceInfo){

    if(sourceInfo == null)
        return "";

    AccessibilityNodeInfo nodeInfo = sourceInfo.getParent().getParent();
    if(nodeInfo!=null){
        int i = 0;
        int i2 = 0;
        for(int i3=0;i3<nodeInfo.getChildCount();i3++){

            try{
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    if(nodeInfo.getChild(i3).getViewIdResourceName().equals("com.flipkart.Android:id/title_layout")) {
                        i = 0;
                        //   while ( i < nodeInfo.getChild(i).getChildCount()){
                        AccessibilityNodeInfo child = nodeInfo.getChild(i3).getChild(i);
                        String charSequence = child.getText().toString();
                        //   Utils.sPrint("Title is : " + charSequence);
                        if(charSequence!=null){
                            return  charSequence;
                        }
                        // }

                    }
                }
               /*
               *
               * Below code to used for getProduct prise in flipkart app product
               *
               * */
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    if(nodeInfo.getChild(i3).getViewIdResourceName().equals("com.flipkart.Android:id/price_layout")){
                        AccessibilityNodeInfo child = nodeInfo.getChild(i3).getChild(i);
                        String charSequence = child.getText().toString();

                        if (child.getText().toString().toUpperCase().startsWith("RS.") ||
                                child.getText().toString().toUpperCase().startsWith("\u20b9")) {
                            String replace = charSequence.replace("Rs.",
                                    "").replace("\u20b9", "");

                            //return replace
                            //   Utils.sPrint("Prise is : " + replace);
                        }

                    }
                }
            }catch (Exception ex){
              //  Utils.sPrint("Ex : " + ex.getMessage());
                return "";
            }

        }
    }
    return "";
}
0
user6443638