Как читать содержимое окна (с помощью accessibilityService) и вызывать пользовательский интерфейс с помощью draw поверх другого разрешения приложения в Android?

мой последние вопрос об одной и той же теме был недостаточно ясен и был отложен сообществом, а позже он был автоматически удален. Поэтому я подробно объясняю этот вопрос, чтобы сообщество могло лучше понять и помочь.

Я хочу функциональность, похожую на Приложение Вуду и MySmartPrice предложения.

теперь, что они делают

Шаг 1: когда мы откройте приложение Voodoo в первый раз, когда они показывают небольшой учебник. В конце урока есть Кнопка" Активировать сейчас", которая при нажатии приведет нас к экрану настроек доступности.

Шаг 2: на экране доступности он далее указывает, как найти и не удалось службу Вуду.

Шаг 3: когда мы включаем его, он также просит предоставить разрешения" наблюдать за вашим действием "и" получить содержимое окна".

шаг 4: как только мы закончим предоставление разрешений на экране доступности, и перейти к какому-либо торговому приложению или получить доступ к торговому сайту через браузер и достичь страницы продукта плавающая кнопка вуду всплывает автоматически.

Шаг 5: как только мы нажмем на него, он показывает цену тех же/связанных/подобных продуктов, доступных на других приложениях или веб-сайте, чтобы пользователь мог проверить лучшее предложение.

ссылка скриншот Вуду!--3-->

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. как я могу показать свой пользовательский интерфейс справки по экрану доступности и странице сведений о продукте.
  2. как чтобы определить, когда страница продукта находится на экране и вызвать мою плавающую кнопку.
  3. как получить имя продукта показано на экране.
  4. как ограничить эту функциональность чтения экрана только для некоторых приложений? (поскольку я не хочу оказаться в какой-то проблеме с авторским правом)
  5. есть ли учебник, который может мне помочь? Хотя я уже пробовал Google для любого прямого учебника и не получил никакого успеха.

теперь зачем мне это нужно информация:

Я планирую приложение для студентов, которое поможет им получить желаемую (или подобную) книгу (электронную книгу) бесплатно, если она доступна на моем сервере или в каком-либо веб-месте, которое доступно в интернете. Я хочу ограничить его функциональность некоторыми приложениями только из-за проблем с авторским правом на некоторые книги.

теперь то, что я знаю из своего исследования по этой теме (но я не уверен, на правильном ли я пути или нет)

2 ответов


Я попытался суммировать, как я смог пройти через эту проблему в блоге. Любой, кому нужна помощь, может в этом разобраться.

служба доступности на следующий уровень

мой предыдущий ответ был удален модераторами может из-за того, что я только что опубликовал ссылку на блог. Поэтому я снова отправляю свой ответ с подробностями здесь.

самый простой способ предоставляется самим 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. Вы должны построить логику с комбинацией родительского, дочернего подсчета, уровня, на котором находятся ваши данные, типа события. Вы должны искать шаблон в событии или источнике или комбинацию вышеуказанных значений, чтобы получить необходимые данные. Кроме того, вы должны настроить некоторые настройте свой код, чтобы получить точные данные в соответствии с вашей задачей. Как и в нашей задаче, у нас есть регулярное выражение для получения ценных данных.

вы должны позаботиться о том, что если представление или UI изменены или идентификаторы res изменены, ваша текущая логика может потерпеть неудачу. Таким образом, вы должны внимательно посмотреть на приложение, на котором вы строите свои услуги. Ниже приведен пример кода, в котором мы получаем данные без идентификатора res, которые могут быть полезны для вас, чтобы понять, как это будет работать.

для источник печати и событие

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

это немного неуклюже, но чтобы заставить его работать, вам нужно заглянуть глубоко в журналы, чтобы найти шаблон, который приведет вас к требуемым данным.

обновление 20/11/2017 Google приостанавливает все приложения используя доступность услуг для доступности. Я получил электронное письмо о том же для одного из моих приложений, и другие разработчики также получают то же самое письмо от google (Reddit). Поэтому я думаю, что мы должны искать какой-то другой способ делать вещи, и я прошу вас обновить его здесь, Если кто-то придумает другую реализацию для достижения такого же поведения, это также поможет другим.

спасибо Umesh Chauhan


Я успешно сделал этот тип приложения, как вуду.и Вам причин заголовок страницы товара, цена и т. д. успешно. следующий метод, используемый для получения экрана пользователя open product.

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