feat(app-glasses): жесты RayNeo SDK (TempleAction) + отключение звуков трекпада
Перенос из 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 не пропадает ✓ (подтверждено пользователем)
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package pw.binom.viewmate.glasses.gestures
|
||||
|
||||
import android.view.MotionEvent
|
||||
import com.rayneo.arsdk.android.touch.FlingArgs
|
||||
import com.rayneo.arsdk.android.touch.TempleAction
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
import pw.binom.viewmate.core.MediaCommand
|
||||
|
||||
/**
|
||||
* Маппинг жестов SDK → команды просмотра (чистая логика, без Android).
|
||||
* FlingArgs требует не-null MotionEvent; в JVM-тесте реальный экземпляр
|
||||
* MotionEvent получаем через sun.misc.Unsafe.allocateInstance (без вызова
|
||||
* конструктора и методов фреймворка — в unit-тестах они "not mocked").
|
||||
* Поля args в templeActionToCommand не читаются — важен только тип жеста.
|
||||
*/
|
||||
class GestureCommandsTest {
|
||||
|
||||
private val flingArgs = FlingArgs(fakeMotionEvent(), fakeMotionEvent(), 0f, 0f)
|
||||
|
||||
@Test
|
||||
fun clickMapsToToggle() {
|
||||
val cmd = templeActionToCommand(TempleAction.Click())
|
||||
assertEquals(MediaCommand.TOGGLE, cmd?.command)
|
||||
assertEquals(0L, cmd?.seekMs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun slideForwardMapsToSeekForward() {
|
||||
val cmd = templeActionToCommand(TempleAction.SlideForward(flingArgs))
|
||||
assertEquals(MediaCommand.SEEK_FORWARD, cmd?.command)
|
||||
assertEquals(30_000L, cmd?.seekMs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun slideBackwardMapsToSeekBackward() {
|
||||
val cmd = templeActionToCommand(TempleAction.SlideBackward(flingArgs))
|
||||
assertEquals(MediaCommand.SEEK_BACKWARD, cmd?.command)
|
||||
assertEquals(30_000L, cmd?.seekMs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun customSeekStepIsUsed() {
|
||||
val cmd = templeActionToCommand(TempleAction.SlideForward(flingArgs), seekStepMs = 60_000)
|
||||
assertEquals(60_000L, cmd?.seekMs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun reservedGesturesMapToNull() {
|
||||
assertNull(templeActionToCommand(TempleAction.DoubleClick()))
|
||||
assertNull(templeActionToCommand(TempleAction.LongClick()))
|
||||
assertNull(templeActionToCommand(TempleAction.TripleClick()))
|
||||
assertNull(templeActionToCommand(TempleAction.MoveUp(false)))
|
||||
assertNull(templeActionToCommand(TempleAction.ActionUp()))
|
||||
assertNull(templeActionToCommand(TempleAction.Idle))
|
||||
}
|
||||
|
||||
private fun fakeMotionEvent(): MotionEvent {
|
||||
val unsafeClass = Class.forName("sun.misc.Unsafe")
|
||||
val theUnsafe = unsafeClass.getDeclaredField("theUnsafe")
|
||||
theUnsafe.isAccessible = true
|
||||
val unsafe = theUnsafe.get(null)
|
||||
val allocate = unsafeClass.getMethod("allocateInstance", Class::class.java)
|
||||
return allocate.invoke(unsafe, MotionEvent::class.java) as MotionEvent
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user