web-dev-qa-db-ja.com

(accessibilityServiceを使用して)ウィンドウのコンテンツを読み取り、Androidの他のアプリのアクセス許可の描画を使用してUIを呼び出す方法は?

同じトピックについての last 質問は十分に明確ではなかったため、コミュニティによって保留にされ、後で自動的に削除されました。それで、私はコミュニティがより良い方法で理解して助けることができるように、その質問を詳細に説明しています。

Voodoo App および MySmartPrice のオファーに似た機能が欲しい。

今何をしているのか

ステップ1:Voodooアプリを初めて開いたときに、小さなチュートリアルが表示されます。チュートリアルの最後にある「今すぐアクティブ化」ボタンを押すと、アクセシビリティ設定画面が表示されます。

ステップ2:アクセシビリティ画面で、Voodooサービスを見つけて無効にする方法をさらにガイドします。

ステップ3:これを有効にすると、「アクションの監視」および「ウィンドウコンテンツの取得」権限の付与がさらに求められます。

ステップ4:アクセシビリティ画面で権限を付与したら、ショッピングアプリに移動するか、ブラウザーを介してショッピングサイトにアクセスし、製品ページにブードゥーフローティングにアクセスしますボタンが自動的にポップアップします。

ステップ5:クリックすると、ユーザーが確認できるように、他のアプリまたはWebサイトで利用可能な同じ/関連する/類似の製品の価格が表示されます最良の取引が可能です。

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

コミュニティから知りたいこと:

  1. アクセシビリティ画面と製品詳細ページにヘルプUIを表示する方法。
  2. 製品ページが画面上にあることを検出し、フローティングボタンを呼び出す方法。
  3. 画面に表示されている商品名を取得する方法。
  4. 一部のアプリのみでこの画面読み上げ機能を制限するにはどうすればよいですか? (私はいくつかの著作権問題に行きたくないので)
  5. 私を助けることができるチュートリアルはありますか?私はすでに直接チュートリアルのためにグーグルで試してみましたが、成功しませんでしたが。

なぜこの情報が必要なのですか:

私のサーバーまたはWebで利用可能なWebの場所で利用できる場合、希望の(または同様の)本(電子ブック)を無料で入手できるようにする学生向けのアプリを計画しています。一部の本の著作権の問題のため、一部のアプリのみに機能を制限したい。

今、私はこのトピックに関する私の研究から知っています(しかし、私が正しい軌道に乗っているかどうかはわかりません)

22
Umesh Chauhan

どうすれば問題を解決できるかをブログの投稿でまとめようとしました。助けが必要な人は誰でもそれを調べることができます。

次のレベルへのアクセシビリティサービス

以前の回答がモデレーターによって削除されました。ブログへのリンクを投稿したばかりである可能性があります。だから私もここに詳細を含めて私の答えをもう一度投稿しています.

最も簡単な方法は、Android自体によって提供されます。独自のアプリで何かを構築している場合、これが最善かつ最も簡単な方法です。説明のとおり、「findAccessibilityNodeInfosByViewId()」を使用する必要があります。未満

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

他のアプリで何かを構築していて、res idがわからない場合。親、子の数、データが見つかったレベル、イベントのタイプの組み合わせでロジックを構築する必要があります。必要なデータを貴重に取得するには、イベント、ソース、または上記の値の組み合わせのパターンを探す必要があります。さらに、タスクに応じて正確なデータを取得するには、コードにTweakを設定する必要があります。タスクのように、貴重なデータを取得するための正規表現があります。

ビューまたはUIが変更された場合、またはres idが変更された場合、現在のロジックが失敗する可能性があることに注意する必要があります。したがって、サービスを構築しているアプリをよく見る必要があります。以下は、res idなしでデータを取得するコードの例です。コードがどのように機能するかを理解するのに役立ちます。

ソースとイベントを印刷するには

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

子供数を取得するには

source.getChildCount();

子供の数が0より大きい場合は、それを調べる必要があるかもしれません。

コード例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;
    }
}

コード例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();
        }
}

少し不器用ですが、機能させるには、ログを詳しく調べて、必要なデータにアクセスするためのパターンを見つける必要があります。

2017年11月20日更新Googleでは、ユーザー補助以外の目的でユーザー補助サービスを使用しているすべてのアプリを停止しています。私のアプリの1つについて同じメールを受け取りましたが、他の開発者もgoogleから同じメールを受け取っています( Reddit )。したがって、他の方法を探す必要があると思います。同じ動作を実現するために他の実装を考え出す人が他にもいる場合は、ここでも更新してください。

ありがとうUmesh Chauhan

26
Umesh Chauhan

私はvoodooのようなこのタイプのアプリケーションを正常に作成し、Flipkart製品ページのタイトル、価格などを正常に取得しました。次のメソッドは、ユーザーが製品画面を開くために使用されます。

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

使用する次のコードは、製品のタイトル名と価格を取得します。

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