6.7 KiB
Задача: масштаб на всех экранах очков + сохранение скролла каталога
Баг 1: ConnectionScreen игнорирует масштаб
Симптом (пользователь): «в режиме НЕ просмотра видео (пока ничего не включено) очки игнорируют выставленный масштаб». Плюс «масштаб будто бы не сохраняется — сразу после старта масштаб 1».
Корень: масштаб (GlassesApp.instance.scale, 0.5..1) применяется ТОЛЬКО в
GlassesMovieScreen.kt (контейнер width((screenWidthPx * scale).toInt().px) +
offset к центру). А ConnectionScreen() в
app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/MainActivity.kt (строка ~354)
рисует контент на ПОЛНУЮ половину глаза (Modifier.width(screenWidthPx.px)) —
scale не читает вообще. Поэтому и «не сохраняется»: сохранённый scale в prefs есть,
но на не-видео экране он не виден.
Фикс: в ConnectionScreen() применять scale так же, как в GlassesMovieScreen:
fun ConnectionScreen() {
val connection = GlassesApp.instance.hostConnection
val connected by connection.connected.collectAsState()
val status by connection.status.collectAsState()
val messages by connection.messages.collectAsState()
val scale by GlassesApp.instance.scale.collectAsState() // ← НОВОЕ
Binocular(scale = false) {
val screenWidthPx = BinocularScreenSize.current.width
val screenHeightPx = BinocularScreenSize.current.height
// масштаб через layout — как в GlassesMovieScreen
val containerW = (screenWidthPx * scale).toInt()
val containerH = (screenHeightPx * scale).toInt()
val offsetX = (screenWidthPx * (1f - scale)).toInt() // сдвиг к носу (левый глаз)
Box(
modifier = Modifier
.width(containerW.px)
.height(containerH.px)
.offset(x = offsetX.px)
.background(Color.Black),
) {
... существующий контент ...
}
}
}
Внутри контейнера всё остаётся: Column с текстами, StatusPanelCompose,
оверлей распознавания — но всё это теперь в уменьшенном контейнере.
Импорт androidx.compose.foundation.layout.offset и width/height уже есть.
⚠️ ВАЖНО: Modifier.offset(x=...) — смещение внутри родителя. Внутри
Binocular(scale=false) контент рисуется на половину 640. При scale<1 контейнер
уже (например 448 при 0.7) и сдвинут к центру (к носу): offsetX = 640*(1-0.7)=192.
Для левого глаза сдвиг вправо (к центру 640), правый глаз — как в GlassesMovieScreen
(offset 0, контейнер прижат к центру слева). Смотри scaleOffsetX в
GlassesMovieScreen.kt:143 — скопировать ту же логику (side из BinocularContent,
там есть Side.LEFT/RIGHT через CompositionLocal).
Баг 2: скролл каталога теряется при навигации
Симптом (пользователь): «кручу вниз до нужной папки, захожу, тыкаю назад (кнопку или аппаратную) и я оказываюсь в предыдущем окне в самом начале (скролл не сохранился). Надо сохранять скролл, даже если зашёл в папку, потом ещё в папку, потом ещё — и т.д.»
Корень: app-phone/src/main/kotlin/pw/binom/viewmate/phone/ui/CatalogScreen.kt:
LazyVerticalGrid (строка 220) БЕЗ LazyGridState — позиция не хранится.
CatalogState живёт на корне (переживает переходы), но позицию скролла не помнит.
Фикс: добавить в CatalogState:
private var scrollIndex by mutableStateOf(0)/scrollOffset by mutableStateOf(0)— текущая позицияprivate val savedScroll = mutableMapOf<Path, Pair<Int, Int>>()— сохранённые позиции по пути- в
openSeries/openSeason/openFolder: сначалаsavedScroll[path] = scrollIndex to scrollOffset, потом менять path - в
back(): после смены path — восстановить(scrollIndex, scrollOffset) = savedScroll[path] ?: (0,0) - методы
setScroll(index, offset)(для snapshotFlow из UI) иgetSavedScroll(): Pair<Int,Int>(текущий path)
В CatalogScreen composable:
val gridState = rememberLazyGridState()
// восстанавливаем позицию при смене пути/загрузке
LaunchedEffect(state.path, state.items) {
val (idx, off) = state.savedScrollForCurrentPath()
gridState.scrollToItem(idx, off)
}
// сохраняем позицию при скролле
LaunchedEffect(gridState) {
snapshotFlow {
gridState.firstVisibleItemIndex to gridState.firstVisibleItemScrollOffset
}.collect { (idx, off) -> state.setScroll(idx, off) }
}
и LazyVerticalGrid(state = gridState, ...).
Ключ сохранения — РОДИТЕЛЬСКИЙ path (тот, откуда ушли). При back() path меняется на родителя — достаём сохранённое. Вложенные папки работают автоматически: каждый уровень сохраняет свою позицию.
Импорты: androidx.compose.foundation.lazy.grid.rememberLazyGridState,
androidx.compose.runtime.snapshotFlow.
Критерии приёмки
- Сборка:
./gradlew :app-phone:assembleDebug :app-glasses:assembleDebug— успех. - Юнит-тесты:
./gradlew :lib-core:testDebugUnitTest :app-phone:testDebugUnitTest :app-glasses:testDebugUnitTest— зелёные. - Ручная проверка на очках: выставить масштаб 0.7 → ConnectionScreen (без видео) тоже уменьшен (чёрные поля по краям, картинка/текст сдвинуты к носу).
- Ручная проверка: скролл каталога вниз → зайти в папку → назад → позиция на месте; то же для вложенных папок.