y83 code / инструмент

Уведомления

Инструмент «Уведомления» включает и выключает системные уведомления приложения по источнику: два агента (Codex, OpenCode) и отдельно — задачи планировщика (cron). Уведомление приходит, когда агент закончил ход или ответил, а приложение свёрнуто; тап по уведомлению открывает нужный экран.

раздел реестра
Приложение
код
253 строк в 1 файлах
документ
35-notifications.md
Раздел: App
Идентификатор: notifications · Экран: ui/home/NotificationSettingsScreen.kt
Снимок: 2026-10-11

1. Зачем и что это

Ключевая особенность: если приложение на переднем плане, уведомление не отправляется вовсе — вместо него звучит системный рингтон уведомления (CodexLifecycle.kt:174). То есть экран управляет именно фоновыми сигналами, а не самой реакцией на завершение хода.

В отличие от большинства экранов хаба, этот использует обычный TopAppBar с заголовком «Notifications», а не терминальную шапку.

2. Как попасть

Хаб инструментов → секция App → карточка «Уведомления» (hub_notifications_title, подпись «Какие агенты могут присылать уведомления», тайл на главной — «Сигналы»).

Откуда ещёПуть
Главнаятайл «Сигналы» (если не скрыт в Настройках главной)
Мостapp_navigate с screen: "notifications"

Маршрут: Screen.kt:153 ("notifications" -> Screen.Notifications) → MainRouter.kt:190 (NotificationSettingsScreen(onBack = …)).

3. Экран: элементы и действия

ЭлементЧто делаетГде в коде
TopAppBar «Notifications»Заголовок, кнопка «назад»NotificationSettingsScreen:56
Секция «Per-agent notifications»Подзаголовок SectionHeaderitem(key = "header")
Пояснение«Enable or disable notifications when a task completes while the app is in background»item(key = "desc")
Карточка CodexSwitch → PREF_NOTIF_CODEX; иконка Codeitem(key = "codex")
Карточка OpenCodeSwitch → PREF_NOTIF_OPENCODE; иконка SmartToyitem(key = "opencode")
Секция «Scheduler»Подзаголовок второй секцииitem(key = "scheduler-header")
Карточка Scheduled runsSwitch → PREF_NOTIF_SCHEDULER; иконка Scheduleitem(key = "scheduler")

Все карточки устроены одинаково (AgentNotifToggle): контейнер surfaceContainerLow, радиус 12.dp, бордер outlineVariant с alpha 0.3, иконка 24.dp (primary при включении, onSurfaceVariant при выключенном), название titleSmall, описание bodySmall, Switch с checkedTrackColor = primary.

4. Возможности подробно

Проверка разрешения

Разрешение POST_NOTIFICATIONS запрашивается отдельно, автоматически, при первом запуске MainActivity.requestNotificationPermission():

  • при SDK < 33 запрос не делается;
  • если разрешение уже выдано — выход;
  • если ключ notif_asked уже установлен — выход (повторно не спрашиваем);
  • иначе ключ выставляется до показа диалога и запускается notificationPermissionLauncher.

Из-за этого порядка отказ пользователя записывается сразу, и приложение больше не спрашивает: чтобы вернуть запрос, нужно вручную выдать разрешение в настройках системы. Экран «Уведомления» не умеет инициировать запрос — только читать и переключать флаги.

Каналы уведомлений

Создаются лениво, через ensureChannel / ensureSchedulerChannel:

КонстантаidИмяВажность
CHANNEL_CHATchat_updatesChat updatesIMPORTANCE_DEFAULT
CHANNEL_SCHEDULERscheduler_runsScheduled runsIMPORTANCE_DEFAULT

Канал создаётся, только если его ещё нет, — то есть переименование или смена важности из системных настроек не перетирается приложением.

Уведомление агента

NotificationHelper.notifyChat(...) формирует PendingIntent на MainActivity с флагами FLAG_ACTIVITY_SINGLE_TOP or FLAG_ACTIVITY_CLEAR_TOP и extras:

ExtraСмысл
agentId, sessionId, title, agentTypeбазовые
threadIdоткрыть нужный тред Codex (код передаёт только Codex)
cwd, projectNameконтекст проекта (передаёт Codex)
screen + taskIddeep link на экран планировщика

Само уведомление: маленькая иконка R.drawable.ic_tile, заголовок, текст с обрезанием до 200 символов в contentText и до 500 в BigTextStyle, setAutoCancel(true). Идентификатор уведомления — sessionId.hashCode() для чата и taskId.hashCode() для планировщика, поэтому на сессию приходит одно уведомление, а не пачка. Отмена — cancelChat(context, sessionId).

Кто вызывает

ВызовТип агента
ui/codex/CodexLifecycle.kt:178codex
ui/opencode/ChatViewModel.kt:572opencode (дефолт)
notifySchedulerпланировщик, флаг PREF_NOTIF_SCHEDULER

Текст уведомления Codex собирается из длительности хода, токенов и статистики (R.string.codex_tokens), с запасным вариантом «Agent finished the turn».

Проверка флага

isEnabled(context, agentType) — единственная точка решения: для известных типов читается соответствующий ключ prefs, для любого другого типа возвращается true (то есть новый агент по умолчанию уведомления шлёт).

5. Данные: БД, файлы, настройки

ЧтоГде лежитКак меняетсяКто читает
Флаг CodexSharedPreferences, notif_codex_enabledТап SwitchNotificationHelper.isEnabled
Флаг OpenCodenotif_opencode_enabledТап SwitchТо же
Флаг планировщикаnotif_scheduler_enabledТап SwitchnotifyScheduler
Флаг «спросили»notif_askedMainActivity.requestNotificationPermissionТам же

Дефолт у всех четырёх — true: уведомления включены из коробки. Синхронизации с сервером нет.

6. Backend: REST/роуты

Неприменимо: уведомления формируются на устройстве.

7. Мост и MCP

ЧтоКакПримечание
Открыть экранapp_navigate с screen: "notifications"BridgeScreenCatalog.kt

Отдельной команды для чтения/смены флагов уведомлений в коде моста не найдено.

8. Настройки, конфиг, квоты и лимиты

ПараметрЗначениеГде задаётся
Каналыchat_updates, scheduler_runsNotificationHelper
Обрезка текста200 символов / 500 в BigTextNotificationHelper
Число источников4 (3 агента + планировщик)Экран
Минимальный SDK для runtime-разрешения33 (Android 13)MainActivity

9. Ловушки и ограничения

  • Все строки экрана захардкожены по-английски («Notifications», «Per-agent notifications», «Codex», «Scheduled runs» и т. д.) — файл не локализован, в отличие от большинства экранов хаба. При русском интерфейсе экран останется английским.
  • Тумблер не даёт разрешение. Включённый флаг при отсутствии POST_NOTIFICATIONS ничего не изменит — уведомлений не будет.
  • Повторный запрос невозможен из приложения. Ключ notif_asked ставится до показа диалога.
  • На переднем плане уведомление не приходит — только рингтон. Если кажется, что уведомления «сломались», сначала проверьте, не было ли приложение открыто.
  • Экран не показывает состояние разрешения системы — только свои флаги, что вводит в заблуждение.
  • Тип агента вне списка всегда true: isEnabled для неизвестного значения возвращает true, поэтому забытый агент будет присылать уведомления.

10. Источники правды

  • app/src/main/java/com/y83/ws/ui/home/NotificationSettingsScreen.kt — экран.
  • app/src/main/java/com/y83/ws/util/NotificationHelper.kt — каналы, extras, отправка.
  • app/src/main/java/com/y83/ws/MainActivity.kt — запрос разрешения, deep link.
  • app/src/main/java/com/y83/ws/ui/codex/CodexLifecycle.kt — вызов при завершении хода.
  • app/src/main/java/com/y83/ws/ui/opencode/ChatViewModel.kt — вызов агента.
  • app/src/main/java/com/y83/ws/App.kt — ключи PREF_NOTIF_*.

Источник: docs/project/hub/35-notifications.md · Приложение · 253 строк в 1 файлах. Страница собирается из документации проекта при сборке сайта.