signingConfigs в app-phone/app-glasses берут кестор и креды из скилла
android-sign (вне git); без кестора release собирается без подписи.
assembleSigned — собрать оба приложения одним шагом.
- glasses: локальный ASR через общий pw.binom.asr (GlassesAsr/GlassesSttSession/SttModelProvider)
- protocol: SttText — очки присылают распознанный текст в телефон; телефон кидает его в LLM
(старый SttAudio/StopStt оставлен фолбэком для старых очей)
- phone: SttStreamer делегирует общему ASR; GlassesServer роутит SttText в LLM
- lib-core: AudioPrep общий
Очки штампуют sentTs в PlaybackPosition; телефон мерит one-way
латентность (receivedTs - sentTs, EWMA) и добавляет её к мастер-позиции
в sync(), снимая системный перекос, из-за которого аудио недокручивало
на величину транспортной задержки. Плюс правки тестов под
HARD_SEEK_THRESHOLD_MS=5000 (остались незакомиченными с прошлого фикса).
- Mercury (app-phone): управление яркостью (с авто-выкл. перед
применением) и WiFi (скан/подключение/список сетей с паролем);
добавлено право BLUETOOTH_ADVERTISE в манифест и runtime-запрос
(без него GATT-сервер не рекламируется → очки не подключаются)
- логирование (lib-core core.log): BatchingLogCollector + LogSink +
LogSpool + LokiLogSink; цепочка очки→телефон→Loki; lib-core log()
переадресован в коллектор
- STT: Qwen3-ASR через pw.binom.asr:asr-qwen3-android, убран in-house
whisper-фоллбек
- audio-sync: P-регулятор с насыщением вместо фиксированной скорости
- media-split: очки только video, телефон только audio (UI-чипы)
- Components: форматирование размера через Locale.ROOT
Если при старте очков телефон недоступен, discoverPhoneUrl возвращал
fallback (эмуляторный 10.0.2.2), он запоминался как lastUrl — очки вечно
реконнектили туда. Теперь на устройстве fallback не используется: не
нашли — повторный поиск через 3с (connectLoop рекурсивно). На эмуляторе
fallback остался. Железо: очки сами нашли телефон после его включения.
Из pair-view: meta-data com.rayneo.mercury.app=true — очки распознают
приложение как AR (без неё открывалось как обычное). Тема
Theme.ViewMateGlasses (чёрный фон, fullscreen), screenOrientation
landscape, hardwareAccelerated. Плюс фикс краша: prefs читались в
конструкторе Application (до attachBaseContext) — перенесено в onCreate.
Проверено на железе: открывается через лаунчер нормально.
На паузе ExoPlayer не шлёт кадры — поверхность меняла размер/позицию,
но кадр оставался старым (часть видео не видно). requestRedraw():
updateTexImage + drawFrame для всех outputs — вызывается при смене
scale (LaunchedEffect) и в surfaceChanged. Подтверждено на железе.
Binocular-контент растягивался на весь 1280 (fillMaxSize внутри
PositionedBox) — панель оказывалась по центру экрана, половина в каждом
глазу. Теперь явная ширина половины (как у видео-Row) — панель по центру
каждой половины, бинокулярно сливается в одну. ConnectionScreen тоже
обёрнут в Binocular. Квадратик-индикатор убран — процент окрашивается
цветом статуса (зелёный/белый/красный). Скрин: обе половины идентичны.
ConnectionScreen был без фона — светлый MaterialTheme. Теперь
darkColorScheme + чёрный Box-корень вокруг обоих экранов + страховка
background(Color.Black) в ConnectionScreen. Проверено скрином:
все пиксели #000000.
Перенос из pair-view (проверено на железе): RayNeoARSDK V0.1.3 (aar в
libs/, MercurySDK.init в GlassesApp), MainActivity → BaseEventActivity,
templeActionViewModel.state → команды (Click=пауза/плей, SlideForward/
Backward=±30с), dispatchTouchEvent→onTouchEvent + toggleSoundEffect(0..3,
false) — системные щелчки трекпада отключены.
Фиксы по ходу (железо):
- applyMediaCommand(msg, onDone) — sendPlaybackPositionNow после seekTo
(иначе телефон получал позицию ДО seek)
- догонка seekTo через 1с на телефоне (первый seekTo в ogg неточен)
- playbackPosition: isPlaying → playWhenReady — ложное playing=false при
буферизации после seekTo паузило звук на телефоне (~5с «пропажи»);
pauseIfPlaying тоже по playWhenReady
Тесты: glasses 59+5, phone 71. Железо: клик пауза/плей ✓, свайпы ±30с ✓,
звук при seek не пропадает ✓ (подтверждено пользователем)