Слои записи и оверлей берут стт-статус и сессию из единого GlassesStt
This commit is contained in:
@@ -14,7 +14,6 @@ import pw.binom.viewmate.core.log.SystemClock
|
||||
import pw.binom.viewmate.glasses.gestures.NavFrame
|
||||
import pw.binom.viewmate.glasses.stt.GlassesStt
|
||||
import pw.binom.viewmate.glasses.ui.layers.AssistantEngine
|
||||
import pw.binom.viewmate.glasses.ui.layers.ListeningLayout
|
||||
import pw.binom.viewmate.glasses.ui.layers.MoveLayout
|
||||
import pw.binom.viewmate.glasses.ui.layers.toFrame
|
||||
|
||||
@@ -135,38 +134,6 @@ class GlassesApp : Application() {
|
||||
prefs.edit().putFloat("vertical", v).apply()
|
||||
}
|
||||
|
||||
// Хостовые STT-события с телефона. Микрофон и распознавание владеет слой записи
|
||||
// [ListeningLayout]: маршрутизирую в *верхний слой* (транспорт о слоях не знает —
|
||||
// сюда заходит из [HostConnection]). Слоя записи нет — событие неактуально, игнор.
|
||||
// Activity о распознавании не знает.
|
||||
|
||||
/** Пустой клик без голоса (SttCancel reason="user") — верхнему слою («не расслышал»). */
|
||||
fun stopSttListening() {
|
||||
val layer = layoutManager.current as? ListeningLayout ?: run {
|
||||
log("stt", "стоп-записи без слоя записи — игнор")
|
||||
return
|
||||
}
|
||||
layer.onSttCancelUser()
|
||||
}
|
||||
|
||||
/** Таймаут тишины 30с с телефона (SttCancel reason="timeout") — верхнему слою. */
|
||||
fun onSttTimeout() {
|
||||
val layer = layoutManager.current as? ListeningLayout ?: run {
|
||||
log("stt", "стт-таймаут без слоя записи — игнор")
|
||||
return
|
||||
}
|
||||
layer.onSttTimeout()
|
||||
}
|
||||
|
||||
/** Хостовая накопленная фраза (SttPhrase.full с телефона) — верхнему слою (живой текст). */
|
||||
fun onSttPhrase(full: String) {
|
||||
val layer = layoutManager.current as? ListeningLayout ?: run {
|
||||
log("stt", "стт-фраза без слоя записи — игнор")
|
||||
return
|
||||
}
|
||||
layer.onSttPhrase(full)
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
|
||||
@@ -40,9 +40,6 @@ 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
|
||||
import pw.binom.viewmate.core.protocol.SttDone
|
||||
import pw.binom.viewmate.core.protocol.SttPhrase
|
||||
import pw.binom.viewmate.core.protocol.Welcome
|
||||
import pw.binom.viewmate.core.protocol.protocolJson
|
||||
|
||||
@@ -368,42 +365,6 @@ class HostConnection(
|
||||
)
|
||||
}
|
||||
|
||||
is SttPhrase -> {
|
||||
// Хостовую накопленную фразу отдаю *верхнему слою* (как и SttCancel):
|
||||
// микрофон/распознавание владеет слой записи, а транспорт о слоях не знает
|
||||
// (роутинг — в [GlassesApp]).
|
||||
GlassesApp.instance.onSttPhrase(msg.full)
|
||||
log("stt", "фраза: ${msg.phrase}")
|
||||
}
|
||||
|
||||
is SttDone -> {
|
||||
log("stt", "ВЕСЬ ТЕКСТ: ${msg.full}")
|
||||
}
|
||||
|
||||
is SttCancel -> {
|
||||
when (msg.reason) {
|
||||
SttCancel.REASON_CANCEL -> {
|
||||
// Явная отмена диктовки двойным тапом: очки САМИ уже сделали стоп
|
||||
// (POP кадра «слушаю» с ОСТАВАНИЕМ в ассистенте — микрофон закрыт,
|
||||
// чат не прерван, оверлей на месте). Это всего подтверждение, что
|
||||
// телефон отменил STT-сессию (stt.reset()) — состояния не трогаем.
|
||||
log("stt", "отмена диктовки (двойной тап) — стоп, остался в ассистенте, оверлей на месте")
|
||||
}
|
||||
SttCancel.REASON_TIMEOUT -> {
|
||||
// Авто-отмена по тишине 30с: пользователь молчит/ушёл — ВЫХОДИМ из
|
||||
// режима (микрофон закрыть, оверлей скрыть), как exitAssistantMode.
|
||||
log("stt", "тишина 30с — выход из режима общения")
|
||||
GlassesApp.instance.onSttTimeout()
|
||||
}
|
||||
else -> {
|
||||
// reason="user" — пустой клик без голоса: не выходим из режима,
|
||||
// стоп-записи с остаованием в ассистенте (idle), «не расслышал».
|
||||
log("stt", "отменено — стоп, остаюсь в ассистенте (не расслышал)")
|
||||
GlassesApp.instance.stopSttListening()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is Shutdown -> {
|
||||
addMessage("Shutdown: закрываю приложение")
|
||||
// как в pair-view: finish() Activity очков
|
||||
@@ -411,6 +372,9 @@ class HostConnection(
|
||||
// страховка: процесс завершится сам, но kill убирает зависшие потоки (RayNeo)
|
||||
android.os.Process.killProcess(android.os.Process.myPid())
|
||||
}
|
||||
|
||||
// Неподдерживаемое host-сообщение — молча игнор (в т.ч. legacy phone-STT: очки не ждут).
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -209,8 +209,8 @@ class MainActivity : BaseEventActivity() {
|
||||
|
||||
// Мозг ассистента (стейт/эффекты) — в движке общения [GlassesApp.assistantEngine]:
|
||||
// слои AssistantLayout / ListeningLayout упираются в него, кино — в MoveLayout.
|
||||
// Хостовые STT-события (SttPhrase/SttCancel) маршрутизирует [GlassesApp] в верхний
|
||||
// слой — Activity о распознавании не знает.
|
||||
// Activity о распознавании не знает: запись самозамкнута в [ListeningLayout] (локальная
|
||||
// стт), host-STT-события очки не обрабатывают (молча игнор в HostConnection).
|
||||
|
||||
/**
|
||||
* Звуки касания трекпада играет сам SDK (GestureDetectorWithSound, SoundPool:
|
||||
|
||||
@@ -30,7 +30,7 @@ data class AssistantState(
|
||||
val stack: List<NavFrame> = listOf(NavFrame.Film),
|
||||
/** «Думаю»: фраза отправлена, ждём ответ (ShowText). Контент кадра ассистента. */
|
||||
val thinking: Boolean = false,
|
||||
/** Накопленные фразы (SttPhrase, full). Контент кадра «слушаю». */
|
||||
/** Живой текст записи (локальное распознавание в [ListeningLayout]). Контент кадра «слушаю». */
|
||||
val sttText: String = "",
|
||||
/** Ответ ассистента (ShowText). Контент кадра ассистента. */
|
||||
val assistantText: String = "",
|
||||
|
||||
@@ -13,9 +13,9 @@ import kotlin.coroutines.resume
|
||||
/**
|
||||
* Слой записи — ЕДИНСТВЕННЫЙ, кто знает про микрофон и распознавание. Он сам открывает
|
||||
* микрофон ([stt.session.start] в [onEnter]), закрывает ([onExit]) и сам принимает
|
||||
* стт-решение: клик распознаёт накопленное ([stt.session.send]), результат/события
|
||||
* (локальный [onSttResult], хостовые [onSttPhrase]/[onSttCancelUser]/[onSttTimeout]) слой
|
||||
* переваривает сам и сообщает вызывающему факт исхода кэлбэками [onSuccess]/[onCancel].
|
||||
* стт-решение: клик распознаёт накопленное ([stt.session.send]), локальный результат
|
||||
* ([onSttResult]) слой переваривает сам и сообщает вызывающему факт исхода кэлбэками
|
||||
* [onSuccess]/[onCancel]. Хостовых входов нет — в локальной архитектуре слой самозамкнут.
|
||||
*
|
||||
* Для всего остального (кино, чат, ассистент) «текст откуда-то берётся» — никто кроме этого
|
||||
* слоя не в курсе микрофона и распознавания. Зависимость — единый объект [GlassesStt] (весь
|
||||
@@ -118,7 +118,7 @@ class ListeningLayout(
|
||||
* - пусто + клик (без голоса) → отмена ([onCancel]) + POP слоя (не расслышал).
|
||||
* Текст дальше «уходит от куда-то» (в LLM на телефон) — слой это не видит.
|
||||
*/
|
||||
fun onSttResult(text: String, auto: Boolean) {
|
||||
private fun onSttResult(text: String, auto: Boolean) {
|
||||
sttText.value = text
|
||||
when {
|
||||
text.isNotBlank() -> {
|
||||
@@ -137,23 +137,4 @@ class ListeningLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Хостовая накопленная фраза (SttPhrase.full): обновить живой текст слоя. */
|
||||
fun onSttPhrase(full: String) {
|
||||
sttText.value = full
|
||||
}
|
||||
|
||||
/** Хостовая отмена (SttCancel, reason="user"): отмена, остаюсь в ассистенте (POP слоя). */
|
||||
fun onSttCancelUser() {
|
||||
log("stt", "хост: отмена (user) → стоп, остаюсь в ассистенте (не расслышал)")
|
||||
onCancel()
|
||||
ctx.close()
|
||||
}
|
||||
|
||||
/** Хостовый таймаут (SttCancel, reason="timeout"): отмена, остаюсь в ассистенте (POP слоя). */
|
||||
fun onSttTimeout() {
|
||||
log("stt", "хост: тишина 30с → отмена, остаюсь в ассистенте")
|
||||
onCancel()
|
||||
ctx.close()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user