Меня зовут Андрей. Я незрячий, и у меня два дисплея Braille eMotion 40. Одним я пользуюсь как основным и на нём же проводил все эксперименты. Второй намеренно оставил в заводском состоянии. Он нужен мне как рабочий запас и точка сравнения, если на основном устройстве что-то пойдёт не так.

Braille eMotion 40: корпус устройства, клавиатура и 40-ячеечная брайлевская строка
Braille eMotion 40: корпус устройства, клавиатура и 40-ячеечная брайлевская строка

Источник: официальная страница HIMS International. © SELVAS Healthcare, Inc., all rights reserved.

Braille eMotion интересен тем, что внутри это полноценное Android-устройство с брайлевской строкой, клавиатурой и собственной оболочкой. Штатных функций мне довольно быстро стало мало. В доступном меню моей версии прошивки не было способа взять обычный APK-файл, установить его и затем управлять установленными программами. Для каждого эксперимента приходилось возвращаться к компьютеру и работать через Android Debug Bridge, или ADB.

Мне хотелось получить нормальный пользовательский сценарий: скопировать APK в папку Download, открыть на самом eMotion диспетчер приложений, прочитать сведения о пакете на брайлевской строке и передать установку системному установщику Android. Для первоначальной установки самого диспетчера я хотел один EXE-файл под Windows, без отдельной настройки Python, Android Studio и ADB.

Так появился Braille eMotion Jailbreak Toolkit. Название осталось от первых опытов, хотя технически это не jailbreak. Проект не получает root-доступ, не разблокирует загрузчик, не меняет прошивку и не обходит защиту платного содержимого. Он использует штатную отладку Android по USB.

Что мешало установить обычное приложение

Сначала задача казалась простой. Braille eMotion работает на Android 12, ADB доступен, значит APK можно отправить стандартной командой установки. Первый пакет действительно установился, но в заводском меню не появился. Получилось приложение, которое есть в системе, однако запустить его с самого устройства неудобно.

После изучения заводской оболочки обнаружилось первое ограничение. Её раздел для дополнительных программ распознаёт пакеты с ожидаемым префиксом com.selvashc.*. Так я сделал раннюю версию диспетчера с подходящим именем пакета. Ярлык появился в разделе «Онлайн библиотеки», зато возникла более неприятная проблема: элементы приложения неправильно читались встроенным скринридером, а фокус вёл себя так, словно перед ним внутренний экран производителя.

Причина оказалась в обработке пакетов самим скринридером. Имена с префиксами com.jawon, com.selvas, com.infraware и com.selvashc считаются частью родной среды. Для штатных экранов это логично, а обычное Android-приложение попадает не в тот режим доступности.

Один и тот же пакет должен был одновременно притвориться родным для оболочки и остаться сторонним для скринридера. Совместить эти требования в одном APK у меня не получилось. Поэтому я разделил решение на две части.

Пакет-мост и основной диспетчер

В заводском меню виден маленький пакет com.selvashc.shortcut.appmanager. У него нет собственного окна. Он сразу запускает основной пакет org.brailleemotion.appmanager и завершает работу. Основной диспетчер уже не подпадает под специальную обработку заводского скринридера и ведёт себя как обычное доступное приложение Android.

Схема получилась такой:

Windows-установщик
    └── ADB
        ├── основной APK: org.brailleemotion.appmanager
        └── пакет-мост:  com.selvashc.shortcut.appmanager

Заводское меню eMotion
    └── «Диспетчер приложений»
        └── пакет-мост
            └── основной диспетчер
                ├── список программ
                ├── системное удаление
                └── APK из Download → системный установщик Android

Код моста умещается в одной Activity. Существенная часть выглядит так:

String targetPackage = getString(R.string.target_package);
Intent launchIntent = getPackageManager()
        .getLaunchIntentForPackage(targetPackage);

if (launchIntent == null) {
    showNotInstalledMessage();
} else {
    launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(launchIntent);
}
finish();

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

Интерфейс, который должен работать без зрения

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

На главном экране диспетчера пять действий:

  1. «Запустить приложение».

  2. «Удалить приложение».

  3. «Установить APK из папки Download».

  4. «Справка».

  5. «Выход».

Список приложений формируется динамически. Для новой программы не требуется создавать ещё один ярлык в заводском меню. Системные пакеты, компоненты HIMS и SELVAS, сама оболочка и диспетчер скрыты из списка удаления. Удаление всё равно подтверждает Android, поэтому случайное нажатие не приводит к немедленному стиранию программы.

Управление проверялось стрелками, Tab, Shift+Tab, Page Up, Page Down, Home, End, Enter, пробелом и Escape. При движении по длинному списку видимая прокрутка синхронизируется с фокусом. Это нужно в том числе зрячему помощнику: он видит на экране тот же пункт, который сейчас читает пользователь.

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

Как устанавливается APK

Диспетчер ищет обычные файлы с расширением .apk непосредственно в /sdcard/Download. Вложенные каталоги он не обходит. Форматы XAPK, APKS и другие наборы split APK пока не поддерживаются.

Перед передачей файла Android выполняются несколько проверок:

  1. Файл повторно разбирается как APK, из него читаются имя, пакет и версия.

  2. Пользователь видит имя файла, размер и сведения о приложении.

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

  4. Подготовленная копия разбирается ещё раз. Пакет и версия должны совпасть с первоначально показанными данными.

  5. Доступ к копии выдаётся системному установщику по одному URI через закрытый для посторонних приложений ContentProvider в режиме только для чтения.

  6. После результата временная копия удаляется. Исходный APK остаётся в Download.

Последнее действие выполняет штатный Package Installer Android:

Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
installIntent.setData(apkUri);
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(installIntent, REQUEST_INSTALL_APK);

Тихой установки здесь нет. Android показывает сведения о приложении и запрашивает подтверждение. При первом запуске также потребуются разрешение на работу с файлами и право устанавливать неизвестные приложения для диспетчера.

В манифесте нет доступа к интернету, контактам, камере, микрофону и геопозиции. Это относится только к самому диспетчеру. Устанавливаемый пользователем APK имеет собственный набор разрешений и собственные риски.

Зачем понадобился автономный установщик для Windows

ADB удобен разработчику, но я не хотел превращать установку в инструкцию из десятка консольных команд. Windows-программа версии 2.0 собрана на Python 3.11 и PyQt5 в один 64-разрядный EXE. Внутри находятся ADB и оба APK, поэтому отдельная среда разработки пользователю не нужна.

Установщик делает больше, чем последовательный запуск adb install:

  • проверяет SHA-256 всех встроенных файлов до любой операции;

  • распознаёт состояния unauthorized и offline, а также подключение нескольких устройств;

  • сверяет модель, производителя, системное имя и наличие заводской оболочки, чтобы не установить набор на обычный телефон;

  • работает только с выбранным серийным номером ADB;

  • сначала ставит основной пакет, затем мост, а при ошибке второго шага откатывает первый;

  • после установки проверяет версии, Activity, мост и, где возможно, хеш установленного APK;

  • возвращает заводскую оболочку в состояние HOME;

  • при удалении затрагивает только пакеты этого проекта.

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

Само окно тоже пришлось проектировать как доступное. У всех интерактивных элементов есть имена, порядок обхода клавишей Tab зафиксирован, журнал можно читать скринридером. Кнопки установки и удаления заблокированы, пока устройство не опознано и пользователь не прочитал предупреждение об ответственности. В диалоге подтверждения фокус сначала получает безопасная кнопка отмены.

У файла нет коммерческой подписи Authenticode, поэтому SmartScreen способен показать предупреждение. Я не советую отключать защиту Windows. Верный порядок действий состоит в загрузке файла со страницы релиза и сравнении его SHA-256 с SHA256SUMS.txt.

Что делал я и что делал GPT Codex

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

Код в репозитории создавался в диалоге с GPT Codex. По моим заданиям Codex анализировал структуру проекта, писал и перерабатывал Java, Python и PowerShell, добавлял автоматические тесты, проверял сборку и готовил документацию. Когда реальное устройство вело себя иначе, чем ожидалось, я приносил журналы и точное описание, после чего мы меняли реализацию и снова проверяли её на eMotion.

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

Для меня такой способ разработки особенно полезен. Графический макет не был источником истины. Источником служили дерево элементов, текстовые журналы, названия контролов, последовательность фокуса и то, что я фактически читал пальцами на брайлевской строке.

Как я проверял результат

На момент подготовки статьи последней опубликованной производителем прошивкой была Braille eMotion V1.5 от 20 апреля 2026 года. Все аппаратные проверки проекта я выполнял на одном Braille eMotion 40 B340 с этой прошивкой и Android 12. Второе моё устройство в тестах не участвовало и осталось в заводском состоянии.

Итоговый прогон 14 августа 2026 года дал такие результаты:

Проверка

Результат

Автоматизированные тесты установщика

33 из 33 пройдены

Запускаемые приложения в длинном списке на eMotion

54 пункта, доступны от начала до конца

APK в тестовой папке Download

9 файлов отображены и прочитаны

Повреждённый файл с расширением .apk

отклонён до установки

Полный цикл удаления и возврата RuStore

версия 1.107.0.3 восстановлена и запускается

Число пакетов до и после цикла Windows-установщика

223 и 223, посторонних изменений нет

Фокусируемые элементы в собранном EXE

17, безымянных среди них нет

Для RuStore я отдельно разрешил проверку с реальным удалением. Приложение было удалено через новый диспетчер, отсутствие пакета проверено через ADB, затем тот же APK установлен из Download и запущен. Остальные пользовательские приложения при этом не трогались.

Я также проверил отказ в разрешениях, отмену системных диалогов, неподходящее ADB-устройство, повреждение встроенного файла, ошибку на втором шаге установки и восстановление после неё. В пройденных сценариях не было FATAL EXCEPTION, а после выхода управление возвращалось в заводскую оболочку.

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

Установка

Исходники, документация и готовый релиз находятся в репозитории проекта на GitHub. Для обычной установки нужен файл Braille-eMotion-AppManager-Setup-2.0.exe со страницы последнего релиза.

Перед началом стоит сохранить документы и настройки eMotion, зарядить устройство и использовать USB-кабель с передачей данных. Дальше порядок такой:

  1. Скачайте EXE и файл SHA256SUMS.txt из одного релиза.

  2. Проверьте контрольную сумму в PowerShell:

    Get-FileHash .\Braille-eMotion-AppManager-Setup-2.0.exe -Algorithm SHA256
    
  3. На eMotion включите параметры разработчика и отладку по USB. Разблокировка загрузчика и беспроводная отладка не нужны.

  4. Подключите только один eMotion. При первом соединении подтвердите системный запрос с отпечатком RSA на самом устройстве.

  5. Запустите EXE, дождитесь состояния «Устройство готово», прочитайте предупреждение и выберите установку.

  6. После сообщения об успехе вернитесь в заводскую оболочку. На проверенной конфигурации пункт «Диспетчер приложений» находится в разделе «Онлайн библиотеки».

Для релиза 2.0.0 SHA-256 установщика равен 9c624bdeb7352479110c8b03d05de9847860d24c5d888309cba23daff06749b7. Если на GitHub уже опубликована другая версия, нужно использовать сумму из её собственного SHA256SUMS.txt, а не значение из этой статьи.

Ограничения и ответственность

Сейчас проект рассчитан на Braille eMotion 40 B340. Основная проверка проведена на Android 12 и прошивке V1.5. Самому диспетчеру требуется Android 8.0 или новее, но это ещё не означает поддержку другого устройства или другой оболочки.

Диспетчер принимает одиночные APK. Split APK, XAPK и APKS не поддерживаются. Он также не может сделать недоступное стороннее приложение удобным для скринридера. Если разработчик APK не разметил элементы интерфейса, диспетчер этого не исправит.

Проект не связан с HIMS или SELVAS и не одобрен ими. В открытом репозитории нет заводской прошивки, закрытых APK производителя и сторонних магазинов. Исходный код опубликован по лицензии MIT.

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

Я сознательно работал только с одним из двух своих дисплеев. На нём диспетчер и Windows-установщик сейчас работают, включая реальный цикл удаления и повторной установки приложения. Второй eMotion остаётся нетронутым напоминанием о простой вещи: даже полезное расширение возможностей не отменяет осторожность.

Если проект пригодится другим владельцам Braille eMotion, буду рад отчётам об испытаниях и сообщениям об ошибках в Issues репозитория. Перед публикацией журнала лучше удалить из него серийный номер устройства, имена файлов и другие личные данные.