Невизуальные приложения: дизайн платформы и компоненты для разработки

Михаил Пожидаев, Алексей Голошумов, Елена Теплых

Научный электронный журнал «Инновации. Наука. Образование», №48, декабрь 2021 г.

Ключевые слова: accessibility, Java, JavaScript, вспомогательные технологии, незрячие, образование

Описывается структура компонентов для создания приложений, ориентированных на восприятие людей без зрения. Основой является платформа LUWRAIn на языке Java, которая демонстрирует существенное упрощение сложности разработки таких приложений. ПОлученные результаты возможно применять при решении образовательных задач и для других целей социализации людей с нарушениями зрения.

Одна из особенностей настоящего периода развития информационных технологий (ИТ) — заметное разнообразие пользовательских интерфейсов. В числе прочего мы видим традиционный графический интерфейс (GUI), управляемый при помощи мыши, сенсорные экраны мобильных устройств, постепенно наступающие голосовые интерфейсы, чатботы, и никуда не ушедшие терминалы и командные строки. Главная причина, по которой появилось столь значительное разнообразие, — постоянное стремление к увеличению уровня удобства пользователя. Успех в этой сфере даёт конкурентные преимущества поставщикам устройств и программного обеспечения (ПО)

При этом традиционно мало внимания уделялось такой сфере, как интерфейсы для людей с нарушениями зрения, хотя даже беглого взгляда на проблему достаточно для понимания, что для незрячих людей влияние пользовательского интерфейса на уровень удобство значительно более выражено, чем для людей, не испытывающих проблем со зрением. Привычным подходом к обеспечению доступности ПО незрячим людям является использование так` называемых утилит экранного доступа, которые читают изменения, происходящие на экране при работе в оконных приложениях. Несмотря на очевидное неудобство этого способа работы (пользователь может управлять компьютером только через клавиатуру, мышь использовать невозможно, что негативно сказывается на скорости работы), существовало мнение, что разработка любых иных методов является неоправданно трудоёмкой.

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

  1. Кроссплатформенность. Приложения такого типа могут запускаться в различных операционных системах (ОС), включая поддержку одноплатных компьютеров Raspberry pi.
  2. Автономность и экономическая эффективность. Приложения не требуют присутствия в системе иных вспомогательных технологий, многие из которых распространяются на коммерческой основе и требуют существенных денежных затрат при легальном использовании.
  3. Простота работы. Интерфейс LUWRAIN максимально учитывает особенности восприятия человека без зрения, что повышает скорость работы и общий уровень комфорта.
  4. Гибкость. LUWRAIN позволяет разрабатывать приложения, наиболее точно удовлетворяющие потребностям людей без зрения, предоставляя также возможность добавления индивидуальных пользовательских расширений на языке JavaScript.

LUWRAIN основан на базе виртуальной машины Java [2] с задействованием компонентов из репозитория Maven Central. Основное преимущество, которое даёт подобное сочетание, — долговечность решений на его основе, потому что любые проекты, которые построены поверх Java и Maven Central слабо подвержены трансформации вычислительных архитектур, а также обладают высокой степенью совместимости с будущими версиями Java и тем самым не потеряют работоспособность с выходом новых версий Java SE (что является очень существенным недостатком таких языков, как, например, Python).

1. Основные понятия и особенности

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

Основное понятие LUWRAIN — область. Объект области — это объект, реализующий интерфейс org.luwrain.core.Area и являющийся отдалённым аналогом понятия окна в Microsoft Windows. Такой объект должен отвечать следующим требованиям:

С точки зрения пользовательского восприятия области поддерживают некоторые специализированные функции. К ним относится прежде всего последовательное прослушивание рабочего пространства от текущего положения фокусной точки. Область может реализовать интерфейс org.luwrain.core.ListenableArea, если она желает как-либо модифицировать процесс чтения по умолчанию, который использует текстовое представление области. Рекомендуется информацию для прослушивания разбивать на логические блоки, при этом ядро системы будет автоматически перемещать положение фокусной точки для индицирования ппрогресса чтения.

Множество областей могут быть совместно скомпонованы для организации интерфейса приложения, предоставляющего определённую функциональность. Объекты приложений должны реализовывать интерфейс org.luwrain.core.Application. Приложения LUWRAIN не являются полноценными приложениями в традиционном смысле этого слова, потому что они не выделяются в отдельные процессы в ОС. Множество всех запущенных приложений пользователя исполняются внутри одной копии виртуальной машины Java. В этом смысле приложения, возможно, правильнее называть апплетами (applets), но оставим изначальный термин, потому что он лучше обозначает функциональное назначение. Объект приложения может также реализовывать интерфейс org.luwrain.core.MonoApp, который обозначает, что приложение может быть запущено только один раз, а при повторном запуске этого приложения наверх должна выноситься уже запущенная копия (например, приложение-кклиент для мессенджера Телеграм нет смысла запускать несколько раз).

При запуске приложения ему передаётся объект, удовлетворяющий интерфейсу org.luwrain.core.Luwrain. Этот объект является главным интерфейсом для взаимодействия приложения с ядром системы. Вместе с тем, ядро способно надёжно идентифицировать приложение, которое выполнило обращение, поскольку для каждого приложения создаётся свой экземмпляр этого объекта. Интерфейс org.luwrain.core.Luwrain содержит все функции, которые так или иначе могут потребоваться для вывода информации, показа всплывающих областей (аналог всплывающих окон в оконном интерфейсе), запуска скриптов JavaScript, синхронизации потоков и пр. Назначение всех операций вполне очевидно по их названиям, но следует остановиться на таких функциях как onAreaNewContent(), onAreaNewHotPoint() и onAreaNewName(). Эти функции должны быть вызваны каждый раз, когда происходит обновление соответствующих частей области (текстового представления, положения фокусной точки или имени). В качестве аргумента в этих функциях должен передаваться указатель на соответствующую область, в которой произошли обновления, а сами значения будут запрошены через соответствующие функции области.

2. Цикл обработки событий и потоковая модель

Центральным компонентом функционирования ядра LUWRAIN является цикл обработки событий. События для обработки поступают из потокобезопасной очереди и разделяются на три следующих типа:

  1. События ввода, т. е. команды пользователя.
  2. Системные события.
  3. Runnable-объекты, которые используются для синхронизации данных между разными потоками и позволяют выполнить произвольное лямбда-выражение вконтексте основного потока системы.

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

Потоковая модель LUWRAIN построена таким образом, что действия, затрагивающие интерфейс пользователя, могут обрабатываться только в контексте основного потока системы. Если на экране должно быть отображено изменение, вызванное фоновым потоком, то код, выполняющий изменение, должен быть помещён в центральную очередь событий для выполнения в контексте основного потока. Графическая часть LUWRAIN создана с использованием библиотеки JavaFX, и основной поток LUWRAIN не является потоком выполнения JavaFX, поэтому между ними необходимо выполнять дополнительную синхронизацию.

Цикл обработки событий в ряде случаев имеет дочерние запуски. Это требуется прежде всего для показа всплывающих областей. Каждая такая область, обычно отображаемая вдоль одной из граней экрана, должна показываться как один вызов функции. Поскольку и сама всплывающая область должна обрабатывать события, внутри функции её показа запускается дополнительный цикл, получающий события из очереди и обрабатывающий их.

3. Слой упрощённой разработки приложений

Пакет org.luwrain.app.base содержит несколько вспомогательных классов, которые упрощают разработку приложений. Его использование не является обязательным, поскольку классы этого пакета не обладают каким-либо привелигированным доступом к функциям ядра, но очень рекомендуемым. Два основных класса этого пакета AppBase и LayoutBase существенно упрощают формирование приложения и позволяют добиться единообразия в поведении элементов управления.

Класс AppBase является шаблоном приложения. Его можно использовать, реализовав только функцию onAppInit(), которая должна инициализировать приложение и вернуть подготовленный объект класса org.luwrain.core.AreaLayout. Этот объект содержит список областей для отображения в интерфейсе запускаемого приложения. Если функция onAppInit() возвращает значение null, то это означает, что приложение не готово к запуску.

Функция runTask() из класса org.luwrain.app.base.AppBase позволяет запустить выполнение некоторой фоновой задачи, которую пользователь может отменить путём нажатия клавиши Escape, и на протяжении выполнения которой воспроизводится специальный контекстный звук, обозначающий, что система занята фоновой задачей. Наиболее распространённым применением функции runTask() являются различные обращения к сетевым сервисам. Подразумевается, что фоновая задача не взаимодействует с системой как-либо, кроме вызова функции finishedTask(), которая принимает новое лямбда-выражение и выполняет его уже в основном потоке системы. Если же пользователь во время выполнения задачи нажимает Escape, то задача считается отменённой, и класс AppBase проследит, чтобы лямбда-выражение, переданное в функцию finishedTask() не было вызвано. Для строгой связи запущенной задачи с результатом её выполнения приложение должно получить специальный идентификатор типа AppBase.TaskId при помощи функции newTaskId(), который следует передать в функции runTask() и finishedTask().

Класс LayoutBase упрощает компоновку комплекта областей для отображения в интерфейсе приложения. Он дополняет объекты-области стандартными функциями. При этом пользователь создаёт все объекты-области самостоятельно, но класс LayoutBase оборачивает их в специальные обёртки. Это накладывает ограничение на создание собственных объектов, реализующих интерфейс org.luwrain.controls.ControlContext, потому что необходима подмена ссылок на области в функциях onAreaNewContent(), onAreaNewHotPoint и onAreaNewName(). Приложение везде должно использовать контекст элемента управления, который создаёт функция LayoutBase.getControlContext(). Класс LayoutBase содержит также функции, которые упрощают создание объектов-областей. В частности, доступны функции editParams(), listParams() и consoleParams(), которые максимально автоматизируют создание объекта Params соответствующих классов. Эти функции принимают лямбда-выражение, которое позволяет вносить корректировки в создаваемый объект параметров без добавления дополнительной переменной в коде приложения.

4. Основные элементы управления

Классы для создания элементов управления находятся в пакете org.luwrain.controls. Объекты элементов управления взаимодействуют с ядром системы при посредничестве объекта, удовлетворяющего интерфейсу org.luwrain.controls.ControlContext. Существует его реализация по умолчанию в классе DefaultControlContext, передающая все запросы в ядро системы, но разрешается любая модификация контекста для получения какого-либо нестандартного поведения, если это необходимо.

4.1. Область навигации

Фундаментальное значение в LUWRAIN имеет область навигации, представленная в классе org.luwrain.controls.NavigationArea. Эта область отвечает за корректное перемещение фокусной точки в текстовом пространстве с построением соответствующего речевого вывода. В большинстве случаев порядок навигации соответствует привычному поведению, но есть и отличия. Например, обработка клавиш Page up и Page down для незрячих людей не имеет смысла в привязке к страницам на экране. Вместо этого пара этих клавиш перемещают фокусную точку по пустым строкам, что часто является разделениями абзацев при редактировании текста. Класс NavigationArea не поддерживает какое-либо редактирование своего содержимого.

4.2. Список

Другим примером элемента управления, имеющего фундаментальное значение для приложений LUWRAIN является список, представленный классом org.luwrain.controls.ListArea. Область этого типа позволяет представить перечисление элементов в виде списка. Класс ListArea содержит интерфейсы Model и Appearance, которые позволяют разнообразным образом управлять содержимым списка и пользовательским восприятием.

Список является основой для ряда других элементов управления, среди которых CommanderArea, EditableListArea и др. При помощи класса ListArea можно создавать не только линейные списки, но и списки, скажем, имеющие двухуровневую иерархию.

4.3. Редактирование текста

Наряду с представлением информации в виде списка редактирование текста играет важнейшую роль в задачах, решаемых приложениями LUWRAIN. Редактирование текста в LUWRAIN разделяется на две основные формы: редактирование однострочного ввода и редактирование многострочного текста. Рассмотрим только редактирование многострочного текста, потому что однострочный режим существенно проще и реализуется по аналогии.

Главное требование к классам, реализующим многострочное редактирование, — возможность дополнения ввода корректорами для ускорения работы. Корректоры должны по возможности сочетаться друг с другом и дополнять друг друга в зависимости от функции объекта редактирования.

Основные операции редактирования текста реализует класс org.luwrain.controls.MultilineEdit. Все действия, выполняемые пользователем, он передаёт в модель в виде следующих операций:

За обработку этих операций отвечает класс org.luwrain.controls.MultilineEditCorrectorTranslator (в будущем будет переименован в EditTranslator), который содержит ссылки на массив строк с положением курсора в них, и вносит необходимые в нихъ изменения в соответствии с логикой перечисленных выше операций.

Между классами MultilineEdit и MultilineEditCorrectorTranslator может находиться произвольное количество корректоров, вложенных один в другой, каждый из которых может модифицировать выполнение операций редактирования, дополняя их новыми операциями нижележащего корректора. Особую роль играет корректор org.luwrain.script.controls.EditCorrectorHooks, который обладает способностью дополнять операции с использованием пользовательских скриптов JavaScript, о которых рассказывается ниже.

Область, представляемая классом org.luwrain.controls.EditArea сочетает функциональность классов NavigationArea и MultilineEdit, первый из которых способен управлять навигацией в текстовом пространстве, но не поддерживает операции редактирования, а второй способен выполнять операции редактирования. В сумме они создают полноценную область для редактирования текста. ДОполнительно к этому класс EditArea позволяет добавлять обработку новых комбинаций клавиш с возможностью делать это при помощи скриптов JavaScript. В частности, в стандартном варианте LUWRAIn дополняет редактирование текста такими комбинациями, как Ctrl+Alt+End — удалить конец строки справа от курсора; Ctrl+Alt+Home — удалить начало строки слева от курсора, Ctrl+Alt+Delete — удалить строку и др.

4.4. Прочие элементы управления

Класс org.luwrain.controls.ConsoleArea является комбинацией NavigationArea с объектом для однострочного ввода, расположенного наверху или внизу области. В таком виде области могут имитировать поведение консоли, а также являться основой для элементов управления, при помощи которых создаются чаты в мессенджерах и социальных сетях.

Класс org.luwrain.controls.TreeArea позволяет представлять различные объекты для пользовательского восприятия в виде иерархической структуры с возможностью свёртывания и развёртывания ветвей. Класс org.luwrain.controls.reader.ReaderArea позволяет представлять пользователю структурированные документы, содержащие материалы в виде гипертекста.

5. Поддержка скриптов JavaScript

В LUWRAIN интегрирована поддержка среды для исполнения JavaScript, основанная на библиотеке GraalVM [3]. Различные компоненты LUWRAIN доступны для использования из программ на JavaScript, что помогает в решении двух задач: позволяет часто модифицируемый код платформы не сохранять в Java-компонентах с возможностью обновления без перекомпиляции и позволяет пользователю вносить свои расширения, не используя инструменты разработки Java.

В целом, процесс интеграции Java и JavaScript достаточно прозрачен и предполагает создание соответствующих обёрток для классов Java, необходимых для доступа к ним из JavaScript. Единственный ньюанс, который требует особенно тщательной проработки — совмещение потоковых моделей. Он связан с тем, что Java — язык, хорошо поддерживающий параллельное исполнение кода, а JavaScript, напротив, не предполагает параллельное исполнение.

Расширение функциональности производится путём добавления так называемых хуков (hooks). Каждый хук — это функция на JavaScript, зарегистрированная в виде лямбда-выражения в скриптовом ядре и ассоциированная с одним из хук-эмиттеров, идентифицируемых по имени. Другими словами, каждому хук-эмиттеру назначается некоторое множество функций для обработки. При этом существует несколько стратегий выполнения функций, назначенных хук-эмиттеру, среди них:

В различных местах своей реализации платформа LUWRAIN содержит разного рода хук-эмиттеры, дополняющие работу системы путём добавления функций к ним на языке JavaScript. В скриптах доступен глобальный объект Luwrain, который является основным способом взаимодействия с системой. Во многом он повторяет функции интерфейса org.luwrain.core.Luwrain.

6. Порядок загрузки расширений

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

В дистрибутивах LUWRAIN присутствуют два каталога для раширений: jar и lib. При запуске системы содержимое этих каталогов сканируется, и загружаются все файлы с расширением *.jar. Подразумевается, что в каталоге jar находятся файлы, которые собраны из исходных текстов проекта, а в lib присутствуют сторонние библиотеки, полученные от других разработчиков в виде бинарных файлов. Фактических различий в порядке обработки файлов между этими каталогами нет, разделение существует сугубо только для удобства и по историческим причинам. При обработке каждого файла с расширением *.jar проверяется его манифест и присутствие секции org/luwrain в нём. Если в этой секции есть значение Extensions, то предполагается, что оно содержит список классов, удовлетворяющих интерфейсу org.luwrain.core.Extension, каждый из которых воспринимается как класс расширения для загрузки. Если такое значение в манифесте отсутствует, то файл загружается просто как вспомогательная библиотека, классы которой будут доступны другим библиотекам.

Пользовательские расширения сохраняются в каталоге extensions в каталоге с пользовательскими данными LUWRAIN (AppData\Roaming\Luwrain в Microsoft Windows и .luwrain в GNU/Linux в домашних каталогах). Внутри каталога extensions просматриваются все подкаталоги, и внутри каждого из них проверяется присутствие подкаталога jar (т. е. проверяются каталоги extensions/*/jar). Далее все файлы *.jarзагружаются по тем же правилам, что и загрузка расширений из каталоговjarиlibв дистрибутиве LUWRAIN, за исключением правила, что группа файлов внутри каждого пользовательского каталогаjar``` загружается со своим отдельным загрузчиком классов (class loader) (на будущее, пока эта возможность отключена).. Подобное правило требуется для обеспечения контроля полномочий расширения и возможности "горячей" выгрузки расширения.

Заключение

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

Другой немаловажный аспект, на который мы хотели бы обратить внимание — это долгосрочность решений такого рода и их политическую автономность. Если вернуться к вопросу поддержки образования, то в настоящий момент в реестре отечественного ПО, составляемым Министерством цифрового развития, связи и массовых коммуникаций, нет ни одной ОС, подходящей для использования в коррекционных школах для детей с нарушениями зрения. Описанный подход не только способен решить проблему построения цифровой образовательной инфраструктуры на базе открытых решений или решений из реестра отечественного ПО, но и расширить круг вычислительной техники, применяемой в образовании, на основе использования встраиваемых компьютеров.

Список литературы

  1. LUWRAIN, основной сайт. https://luwrain.org
  2. Java. https://www.java.com/ru/
  3. GraalVM. https://www.graalvm.org/