Регулятор «Высота» экрана очков (0..1, с учётом масштаба): SetVertical по протоколу, offset Y в кино/коннект/оверлее, слайдер на пульте
This commit is contained in:
@@ -52,6 +52,9 @@ class GlassesApp : Application() {
|
||||
/** Масштаб экрана очков (0.5..1.0). Загружается в onCreate (prefs — после привязки контекста). */
|
||||
val scale = MutableStateFlow(1f)
|
||||
|
||||
/** Вертикаль экрана очков (0..1): 1.0 — у верха (как всегда было), 0.0 — нижняя кромка у низа. */
|
||||
val vertical = MutableStateFlow(1f)
|
||||
|
||||
/** Идёт распознавание речи (TripleClick стартовал, Click/DoubleClick/тишина завершили). */
|
||||
val listening = MutableStateFlow(false)
|
||||
|
||||
@@ -92,10 +95,18 @@ class GlassesApp : Application() {
|
||||
prefs.edit().putFloat("scale", v).apply()
|
||||
}
|
||||
|
||||
/** Применить и сохранить вертикаль (0..1). */
|
||||
fun applyVertical(value: Float) {
|
||||
val v = value.coerceIn(0f, 1f)
|
||||
vertical.value = v
|
||||
prefs.edit().putFloat("vertical", v).apply()
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
scale.value = prefs.getFloat("scale", 1f).coerceIn(0.5f, 1f)
|
||||
vertical.value = prefs.getFloat("vertical", 1f).coerceIn(0f, 1f)
|
||||
MercurySDK.init(this)
|
||||
movieController = MovieController(applicationContext)
|
||||
movieController.eventLogger = { msg -> logToFile("mc: $msg") }
|
||||
|
||||
@@ -36,6 +36,7 @@ import pw.binom.viewmate.core.protocol.PlayVideo
|
||||
import pw.binom.viewmate.core.protocol.PlaybackPosition
|
||||
import pw.binom.viewmate.core.protocol.SetMode
|
||||
import pw.binom.viewmate.core.protocol.SetScale
|
||||
import pw.binom.viewmate.core.protocol.SetVertical
|
||||
import pw.binom.viewmate.core.protocol.Shutdown
|
||||
import pw.binom.viewmate.core.protocol.ShowText
|
||||
import pw.binom.viewmate.core.protocol.SttCancel
|
||||
@@ -309,6 +310,11 @@ class HostConnection(
|
||||
log("app", "scale → ${msg.scale}")
|
||||
}
|
||||
|
||||
is SetVertical -> {
|
||||
GlassesApp.instance.applyVertical(msg.vertical)
|
||||
log("app", "vertical → ${msg.vertical}")
|
||||
}
|
||||
|
||||
is PlayVideo -> {
|
||||
addMessage("получено видео: «${msg.title}» — ${msg.videoUrl}")
|
||||
reconnectResume.reset()
|
||||
|
||||
@@ -65,6 +65,7 @@ import pw.binom.viewmate.glasses.ui.GlassesMovieScreen
|
||||
import pw.binom.viewmate.glasses.ui.Side
|
||||
import pw.binom.viewmate.glasses.ui.px
|
||||
import pw.binom.viewmate.glasses.ui.scaleOffsetX
|
||||
import pw.binom.viewmate.glasses.ui.verticalOffsetY
|
||||
|
||||
class MainActivity : BaseEventActivity() {
|
||||
private var resumeAfterScreenOn = false
|
||||
@@ -458,6 +459,7 @@ fun ConnectionScreen() {
|
||||
val status by connection.status.collectAsState()
|
||||
val messages by connection.messages.collectAsState()
|
||||
val scale by GlassesApp.instance.scale.collectAsState()
|
||||
val vertical by GlassesApp.instance.vertical.collectAsState()
|
||||
|
||||
// Бинокулярно: контент дублируется в оба глаза, панель — по центру каждой
|
||||
// половины (явная ширина половины, иначе fillMaxSize растянет на 1280).
|
||||
@@ -475,7 +477,10 @@ fun ConnectionScreen() {
|
||||
modifier = Modifier
|
||||
.width(containerW.px)
|
||||
.height(containerH.px)
|
||||
.offset(x = scaleOffsetX(side, screenWidthPx, scale).px)
|
||||
.offset(
|
||||
x = scaleOffsetX(side, screenWidthPx, scale).px,
|
||||
y = verticalOffsetY(screenHeightPx, scale, vertical).px,
|
||||
)
|
||||
.background(Color.Black),
|
||||
) {
|
||||
Column(
|
||||
|
||||
@@ -91,6 +91,7 @@ private fun overlayBox(
|
||||
) {
|
||||
Binocular(scale = false) {
|
||||
val scale by app.scale.collectAsState()
|
||||
val vertical by app.vertical.collectAsState()
|
||||
val screenWidthPx = BinocularScreenSize.current.width
|
||||
val screenHeightPx = BinocularScreenSize.current.height
|
||||
val side = BinocularScreenSide.current
|
||||
@@ -100,7 +101,10 @@ private fun overlayBox(
|
||||
modifier = Modifier
|
||||
.width(containerW.px)
|
||||
.height(containerH.px)
|
||||
.offset(x = scaleOffsetX(side, screenWidthPx, scale).px)
|
||||
.offset(
|
||||
x = scaleOffsetX(side, screenWidthPx, scale).px,
|
||||
y = verticalOffsetY(screenHeightPx, scale, vertical).px,
|
||||
)
|
||||
.background(Color.Black.copy(alpha = 0.72f)),
|
||||
) {
|
||||
AssistantOverlayContent(state, chatHistory, scrollState)
|
||||
|
||||
@@ -57,6 +57,7 @@ fun GlassesMovieScreen(
|
||||
var videoHeight by remember { mutableIntStateOf(480) }
|
||||
|
||||
val scale by GlassesApp.instance.scale.collectAsState()
|
||||
val vertical by GlassesApp.instance.vertical.collectAsState()
|
||||
|
||||
// Один рендерер на экран: оба экземпляра BinocularVideoPlayer (левый/правый
|
||||
// глаз из Binocular) регистрируют в нём свои SurfaceView.
|
||||
@@ -107,7 +108,10 @@ fun GlassesMovieScreen(
|
||||
modifier = Modifier
|
||||
.width(containerW.px)
|
||||
.height(containerH.px)
|
||||
.offset(x = scaleOffsetX(side, screenWidthPx, scale).px),
|
||||
.offset(
|
||||
x = scaleOffsetX(side, screenWidthPx, scale).px,
|
||||
y = verticalOffsetY(screenHeightPx, scale, vertical).px,
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().background(Color.Black),
|
||||
@@ -143,6 +147,14 @@ fun GlassesMovieScreen(
|
||||
fun scaleOffsetX(side: Side, screenWidthPx: Int, scale: Float): Int =
|
||||
if (side == Side.LEFT) (screenWidthPx * (1f - scale)).toInt() else 0
|
||||
|
||||
/**
|
||||
* Вертикальное смещение контейнера глаза (с учётом масштаба):
|
||||
* vertical = 1.0 — у верха (как всегда было); 0.0 — нижняя кромка контейнера
|
||||
* прижата к низу экрана. Свободный ход по вертикали = (1 - scale) высоты.
|
||||
*/
|
||||
fun verticalOffsetY(screenHeightPx: Int, scale: Float, vertical: Float): Int =
|
||||
((1f - vertical) * screenHeightPx * (1f - scale)).toInt()
|
||||
|
||||
/** Отладочный режим: красный квадрат вместо видео (проверка масштаба/позиций скрином). Перед финалом — false. */
|
||||
private const val SCALE_DEBUG_SQUARE = false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user