diff --git a/app-glasses-vm/src/main/kotlin/pw/binom/rayneovm/glasses/protocol/VmGlassesController.kt b/app-glasses-vm/src/main/kotlin/pw/binom/rayneovm/glasses/protocol/VmGlassesController.kt index 163bf4a..c8051fe 100644 --- a/app-glasses-vm/src/main/kotlin/pw/binom/rayneovm/glasses/protocol/VmGlassesController.kt +++ b/app-glasses-vm/src/main/kotlin/pw/binom/rayneovm/glasses/protocol/VmGlassesController.kt @@ -6,6 +6,7 @@ import android.content.Intent import android.content.IntentFilter import android.graphics.SurfaceTexture import android.os.IBinder +import android.os.SystemClock import android.util.Log import android.view.Surface import androidx.compose.runtime.staticCompositionLocalOf @@ -182,12 +183,15 @@ class VmGlassesController { scrollPump = testScope.launch { var accX = 0f var accY = 0f + var lastDeltaAt = 0L while (isActive) { delay(SCROLL_TICK_MS) + var got = false while (true) { val r = scrollChannel.tryReceive().getOrNull() ?: break accX += r[0] accY += r[1] + got = true } val src = activeSource val pos = _cursor.value @@ -196,6 +200,16 @@ class VmGlassesController { accY = 0f continue } + val now = SystemClock.uptimeMillis() + if (got) lastDeltaAt = now + // Копили мало и пауза — сбрасываем хвост, иначе он выстрелит в следующем жесте. + if (!got && now - lastDeltaAt > SCROLL_IDLE_RESET_MS) { + accX = 0f + accY = 0f + continue + } + // Драг короче ~90 px гость считает кликом по элементу списка, а не скроллом, + // поэтому короткие пачки продолжаем копить. if (abs(accX) < SCROLL_MIN_PX && abs(accY) < SCROLL_MIN_PX) continue val ok = ShizukuVdBinder.scroll( binder = src.shizukuBinder, @@ -398,9 +412,14 @@ class VmGlassesController { private const val VIRTUAL_HEIGHT = VmDisplay.HEIGHT private const val VIRTUAL_DENSITY = VmDisplay.DENSITY - /** Скролл-драг: как часто сливаем накопленные дельты и минимальный порог (px). */ + /** Скролл-драг: как часто сливаем накопленные дельты, порог и сброс «хвоста». */ private const val SCROLL_TICK_MS = 90L - private const val SCROLL_MIN_PX = 16f + + /** Минимальная длина драга: короче гость трактует как клик по элементу (проверено на Settings ~50-60 px). */ + private const val SCROLL_MIN_PX = 96f + + /** Если дельты пропали дольше этого — накопленное не тащим в следующий жест. */ + private const val SCROLL_IDLE_RESET_MS = 300L /** Активность RayNeo-лаунчера, чей `onStart` включает вендорский `tmpStopBackgroundKill`. */ private const val GUARD_PACKAGE = "com.ffalconxr.mercury.launcher" diff --git a/app-phone-vm/src/main/kotlin/pw/binom/rayneovm/phone/ui/RemoteScreen.kt b/app-phone-vm/src/main/kotlin/pw/binom/rayneovm/phone/ui/RemoteScreen.kt index 17e4797..fa86e1f 100644 --- a/app-phone-vm/src/main/kotlin/pw/binom/rayneovm/phone/ui/RemoteScreen.kt +++ b/app-phone-vm/src/main/kotlin/pw/binom/rayneovm/phone/ui/RemoteScreen.kt @@ -88,6 +88,7 @@ fun RemoteScreen( val gainY = VmDisplay.HEIGHT / size.height.toFloat() var mode = PadMode.UNDECIDED var lastCentroid = Offset.Zero + var lastPressedCount = 1 var travel = 0f awaitFirstDown(requireUnconsumed = false) @@ -105,6 +106,14 @@ fun RemoteScreen( if (mode == PadMode.UNDECIDED || (mode == PadMode.CURSOR && pressed.size >= 2)) { mode = if (pressed.size >= 2) PadMode.SCROLL else PadMode.CURSOR lastCentroid = centroid + lastPressedCount = pressed.size + continue + } + // Число пальцев изменилось (отпустили один из двух) — центроид скачет, + // этот скачок не скроллим, иначе список прыгает. + if (pressed.size != lastPressedCount) { + lastPressedCount = pressed.size + lastCentroid = centroid continue }