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