VEU.WathBody/1-docs/4-modules/m12-watch-channel/api_specs/04-ui-integration.md

108 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Спецификация 04: UI — «Тренировка», «Допуск», «Настройки» (интеграция m10)
Источники: Ф-3.1–3.3, Ф-13.2, ui-ux разд. 2, module_reqs разд. 2,
решения 3/5/6. Единицы 4–7. Все тексты дублируют цвет (🛡).
## 1. Экран «Тренировка» (замена заглушек WorkoutScreen:380/258)
### Статусная строка (Ф-3.1)
- слева направо: «часы: <статус> (заряд N %)» · «пульс: <bpm|—>» ·
«сеть: онлайн/офлайн» (сеть — как в m10);
- тексты и цвета — по таблице спец 03 §1; зелёный/красный/серый
— только дублирование текстом;
- заряд опускается (с прочерком) при `battery == null` —
формулировка «часы: связь есть», без «заряд —» (решение 3).
### Блок пульса (Ф-3.2)
- текущее значение — крупно (≥40 sp) в существующей зоне статуса,
окраска по зонам Танака (`ZoneCalculator` m10, `Hr.MaxBpm`;
границы — те же, что на «Профиле»): зона 1 recovery <50 % синий,
зона 2 лёгкая 50–70 % зелёный, зона 3 средняя 70–85 % жёлтый,
зона 4 высокая >85 % красный — рядом с числом название зоны
словами, как на «Профиле» m10 («recovery»/«лёгкая»/«средняя»/
«высокая») 🛡;
- под значением — линия последнего подхода: Canvas по
`store.series(lastSetAtMs, now)`, обновление ~1 Гц, без сеток;
пусто/устарело — прочерк «—» вместо линии;
- `lastSetAtMs` — время последнего подхода текущей тренировки
(VM из `setsFlow`, при отсутствии подходов — старт тренировки);
- нет связи/не выбраны/нет разрешения — значение «—», блок
сворачивается в статус-строку; голос и ручной ввод не зависят
(Ф-1.1, Ф-3.2).
### Финиш
- «Завершить» → `finishWorkout(id, avgHr = channel.avgHr(now))`;
после записи `store.clear()`; при null среднего — как в m09
(`avg_hr` не отправляется).
## 2. Экран «Допуск» (Ф-13.2)
- под полем «Пульс сидя» — вторичная кнопка «Взять с часов»:
активна при `freshHr(now) != null` (свежий сэмпл ≤30 с),
нажатие подставляет значение в поле (пикером остаётся правка);
нет сэмпла — кнопка неактивна с подписью причины («часы не
связаны» / «нет свежего значения»);
- экран держит канал открытым (`bind(null)`), для допуска
достаточно пульса покоя — атлет включает трансляцию заранее
(инструкция в «Настройках»).
## 3. Экран «Настройки» — блок «Часы»
По образцу блока «Распознавание речи» (m11):
- строка статуса канала (текст из спец 03 §1);
- «Найти часы» — скан 15 с по UUID 0x180D (запрашивает разрешения
при необходимости), список найденных: имя + адрес, тап — выбор
(запись `Watch.HrDeviceAddress`, индикатор «выбрано»);
- «Забыть часы» — сброс адреса (`""` → NOT_SELECTED);
- инструкция включения трансляции — платформо-нейтральный текст:
«Включите на часах трансляцию пульса по Bluetooth» +
расширяемый справочник производителей (v1 — один пункт:
Garmin: «виджет пульса → удержание MENU → Heart Rate Options →
Broadcast During Activity = Вкл»); справочник — `map` в коде
UI (не БД), добавление производителя — правкой одного списка;
- никаких маркателей конкретных моделей в логике.
## 4. Разрешения (runtime)
- экраны «Тренировка»/«Допуск»/«Настройки»: при статусе
NO_PERMISSION — строка «Нет разрешения Bluetooth» + кнопка
«Разрешить» → системный диалог
(12+: `BLUETOOTH_SCAN`+`BLUETOOTH_CONNECT` вместе; 8–11:
`ACCESS_FINE_LOCATION` с пояснением «для поиска часов»);
«Больше не спрашивать» — кнопка ведёт в системные настройки
приложения (проверка после возврата);
- отказ не блокирует экраны (🛡 инвариант 2): статус-строка
показывает причину, остальное работает.
## 5. Манифест
```xml
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```
(`BLUETOOTH_CONNECT` — без `neverForLocation`, только SCAN.)
## 6. Тесты (pure JVM)
- форматирование статус-строки: все WatchStatus × батарея
есть/нет × пульс есть/устарел (тексты из спец 03 §1, заряд
опускается при null);
- зона пульса → слово-зона и цветовой токен (границы 50/70/85 %
от Hr.MaxBpm по ZoneCalculator);
- кнопка «Взять с часов»: active/inactive по freshHr, подстановка
значения;
- логика «Больше не спрашивать» — на уровне VM-флага (системный
диалог — ручная приёмка);
- Compose-иерархия — без unit (ручной смок владельца, критерий 3).