Служба специальных возможностей Android — обновления экрана несовместимы

У меня есть Android AccessibilityService, развернутый на Samsung Note 4 под управлением Android 5.0.1.

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

Событие 2048 (TYPE_WINDOW_CONTENT_CHANGED) не всегда вызывается Android. Если я отправляю сообщения в свой WhatsApp, когда он находится в фокусе, а экран включен в 75% случаев, это событие запускается, а иногда просто не запускается.

Есть причина для этого? Являются ли события доступности независимыми?

Кроме того, похоже, что событие 4096 (TYPE_VIEW_SCROLLED) запускается постоянно, когда пользователь прокручивает или когда в окне чата WhatsApp появляется новая корреспонденция, однако, похоже, нет возможности определить, какова текущая позиция прокрутки устройства? AccessibilityEvent.getSource() предоставляет доступ к некоторым метаданным для списка (в данном случае android:id/list), однако отсутствует информация о позиции прокрутки этого списка или его дочерних элементов. Дочерний список зависит от того, что отображается на экране, а значения boundsToScreen/Parent одинаковы независимо от того, смотрите ли вы в конец списка, в середину или вверх. Есть ли какие-нибудь подсказки, которые помогут мне определить положение прокрутки из экземпляра AccessibilityEventNodeInfo, который мне представлен?

Наконец, когда событие 2048 (TYPE_WINDOW_CONTENT_CHANGED) действительно срабатывает, бывают случаи, когда новые элементы фактически недоступны из AccessibiltyEvent.getSource() (даже если вы выполняете итерацию до корневого элемента через цикл while, используя getParent(), а затем снова сканируете вниз). Похоже, что событие делает снимок экрана до того, как изменение было применено к пользовательскому интерфейсу. thread.sleep не помогает - похоже, что AccessibilityEventNodeInfo - это скорее снимок, чем прямой доступ к пользовательскому интерфейсу? Как-нибудь обойти это?


comment
Вы когда-нибудь решили эту проблему, я испытываю то же самое.   -  person Sydwell    schedule 29.06.2021


Ответы (1)


Есть причина для этого? Являются ли события доступности независимыми?

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

Thread.sleep не помогает - как кажется, AccessibilityEventNodeInfo - это скорее снимок, чем прямой доступ к пользовательскому интерфейсу? Как-нибудь обойти это?

Это также объясняет, почему элементы отсутствуют в источнике события. Вы получаете событие перед отрисовкой динамических элементов. Таким образом, запускается новое действие и инициализируется с новым содержимым, однако приложение, вероятно, переходит в режим ожидания для некоторого типа сетевых/REST-ресурсов. Перед поступлением этих ресурсов событие запускается, а вскоре после этого отрисовывается новый контент. Таким образом, вам кажется, что вы получаете неполный контент, но на самом деле вы получаете полный контент на момент запуска события. Ваш метод thread.sleep работает отлично. Однако после сна вы не можете проверить значение event.getSource(), сон не изменит то, что было передано этой функции. Вместо этого вы хотите заснуть, а затем просмотреть всю иерархию представлений, чтобы найти нужную информацию. Выспитесь, а затем используйте

getRootInActiveWindow();

на месте

event.getSource();

Есть ли какие-либо подсказки, которые помогут мне определить положение прокрутки из экземпляра AccessibilityEventNodeInfo, который мне представлен?

Да и нет. Нет, служба специальных возможностей не может определить расположение полосы прокрутки. Однако вы можете определить приблизительное положение события прокрутки, выполнив следующие действия:

private float getScrollPosition(AccessibilityEvent event) {
    final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event);
    final int itemCount = event.getItemCount();
    final int fromIndex = event.getFromIndex();

    // First, attempt to use (fromIndex / itemCount).
    if ((fromIndex >= 0) && (itemCount > 0)) {
        return (fromIndex / (float) itemCount);
    }

    final int scrollY = record.getScrollY();
    final int maxScrollY = record.getMaxScrollY();

    // Next, attempt to use (scrollY / maxScrollY). This will fail if the
    // getMaxScrollX() method is not available.
    if ((scrollY >= 0) && (maxScrollY > 0)) {
        return (scrollY / (float) maxScrollY);
    }

    // Finally, attempt to use (scrollY / itemCount).
    // TODO(alanv): Hack from previous versions -- is it still needed?
    if ((scrollY >= 0) && (itemCount > 0) && (scrollY <= itemCount)) {
        return (scrollY / (float) itemCount);
    }

    return 0.5f;
}

Это прямо из кода Google TalkBack. Вы можете найти его в ScrollFormatter.java в проекте EyesFree. Это не идеальное решение. Если событие происходит из большого макета (что часто случается с веб-страницами в Chrome), вы можете получить тот же результат для большой части прокрутки. Однако API не поддерживают ничего более точного, поэтому это необходимый хак, если вы хотите знать приблизительное местоположение прокрутки. Я думаю, что этот метод можно значительно улучшить, даже при наличии доступных API, хотя это потребует некоторой работы.

person ChrisCM    schedule 26.02.2016