Подтвердить что ты не робот

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

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

Мне нужна функциональность, аналогичная предложениям Voodoo App и MySmartPrice.

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

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

Шаг 2: На экране специальных возможностей, он также содержит инструкции по поиску и отключению службы Voodoo.

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Распечатать источник и событие

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

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

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

Спасибо Умеш Чаухан

Ответ 2

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

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