26

一个关于同一主题的问题不够清楚,被社区搁置,后来被自动删​​除。因此,我正在详细解释该问题,以便社区可以更好地理解和帮助。

我想要类似于Voodoo 应用程序MySmartPrice提供的功能。

现在他们做什么

第 1 步:当我们第一次打开 Voodoo 应用程序时,它们会显示一个小教程。在教程结束时,有一个“立即激活”按钮,按下该按钮会将我们带到辅助功能设置屏幕。

第 2 步:在辅助功能屏幕中,它进一步指导如何查找和禁用 Voodoo 服务。

第 3 步:当我们启用它时,它会进一步要求授予“观察您的操作”和“检索窗口内容”权限。

第 4 步:一旦我们在可访问性屏幕上授予权限,并移动到某个购物应用程序或通过浏览器访问购物网站并到达产品页面,就会自动弹出一个 voodoo 浮动按钮。

第 5 步:一旦我们点击它,它会显示在其他应用程序或网站上可用的相同/相关/相似产品的价格,以便用户可以检查可用的最佳交易。

Voodoo 参考截图

巫毒屏幕 1

辅助功能屏幕

辅助功能屏幕 2

权限授予屏幕

巫毒屏幕 2

产品页面上的 Voodoo 浮动按钮

借助其他应用程序绘制 Voodoo 结果

现在我想从社区中了解:

  1. 如何在辅助功能屏幕和产品详细信息页面上显示我的帮助 UI。
  2. 如何检测产品页面何时出现在屏幕上并唤起我的浮动按钮。
  3. 如何获取屏幕上显示的产品名称。
  4. 如何仅针对某些应用程序限制此屏幕阅读功能?(因为我不想最终遇到一些版权问题)
  5. 有什么教程可以帮助我吗?尽管我已经在 Google 上尝试过任何直接教程,但没有取得任何成功。

现在为什么我需要这些信息:

我正在为学生计划一个应用程序,如果它在我的服务器上或在一些可用的网络位置上可用,它将帮助他们免费获得所需的(或类似的)书籍(电子书)。由于某些书籍的版权问题,我只想将其功能限制为某些应用程序。

现在我从关于这个主题的研究中了解到(但我不确定我是否走在正确的轨道上)

4

2 回答 2

28

我曾试图在一篇博文中总结我是如何解决这个问题的。任何需要帮助的人都可以看看它。

无障碍服务更上一层楼

我之前的回答被版主删除了,可能是因为我刚刚发布了一个博客链接。所以我也在这里再次发布我的答案和详细信息。

最简单的方法是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。您必须结合父级、子级计数、找到数据的级别、事件类型来构建逻辑。您必须在事件或源中寻找模式或上述值的组合,以获取所需的数据。此外,您必须在代码中进行一些调整,以根据您的任务获取准确的数据。就像在我们的任务中一样,我们有一些正则表达式来获取宝贵的数据。

您必须注意,如果视图或 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() + "");
}

获取 childcount

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 将暂停所有使用辅助功能服务的应用程序,而不是辅助功能。我收到了一封关于我的一个应用程序的电子邮件,其他开发人员也收到了来自谷歌(Reddit)的相同电子邮件。所以,我认为我们应该寻找一些其他的做事方式,我请求你也请在这里更新它,如果有人提出其他实现来实现相同的行为,它也会帮助其他人。

谢谢 Umesh Chauhan

于 2016-05-23T10:40:05.533 回答
0

我已经成功地制作了像 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 "";
}
于 2016-08-22T11:56:48.833 回答