CICD: сборка APK на release → F-Droid; versionCode из тега; фикс скролла каталога и масштаба
build / Android APK to F-Droid (release) Failing after 36s
build / Android APK to F-Droid (release) Failing after 36s
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# Задача: масштаб на всех экранах очков + сохранение скролла каталога
|
||||
|
||||
## Баг 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:
|
||||
|
||||
```kotlin
|
||||
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:
|
||||
```kotlin
|
||||
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`.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
1. Сборка: `./gradlew :app-phone:assembleDebug :app-glasses:assembleDebug` — успех.
|
||||
2. Юнит-тесты: `./gradlew :lib-core:testDebugUnitTest :app-phone:testDebugUnitTest :app-glasses:testDebugUnitTest` — зелёные.
|
||||
3. Ручная проверка на очках: выставить масштаб 0.7 → ConnectionScreen (без видео) тоже уменьшен (чёрные поля по краям, картинка/текст сдвинуты к носу).
|
||||
4. Ручная проверка: скролл каталога вниз → зайти в папку → назад → позиция на месте; то же для вложенных папок.
|
||||
Reference in New Issue
Block a user