package pw.binom.viewmate.glasses import android.app.Application import android.content.Context import com.rayneo.arsdk.android.MercurySDK import java.io.File import kotlinx.coroutines.flow.MutableStateFlow /** Конфигурация очков: адрес WS-сервера телефона. Креды не нужны — очки просто экран. */ object GlassesConfig { // Fallback: эмулятор — 10.0.2.2 (localhost хоста); на устройстве телефон // находится авто-обнаружением по mDNS (_viewmate._tcp). const val HOST_URL = "ws://10.0.2.2:8080/ws/glasses" } /** * Application очков: создаёт и запускает HostConnection (WS-клиент к телефону). * Синглтон-доступ через companion. */ class GlassesApp : Application() { lateinit var hostConnection: HostConnection private set lateinit var movieController: MovieController private set /** Менеджер скачивания контента в память очков (отправку на телефон — через HostConnection). */ lateinit var downloadManager: GlassesDownloadManager private set private val prefs by lazy { getSharedPreferences("viewmate_prefs", Context.MODE_PRIVATE) } /** Масштаб экрана очков (0.5..1.0). Загружается в onCreate (prefs — после привязки контекста). */ val scale = MutableStateFlow(1f) /** Идёт распознавание речи (TripleClick стартовал, Click/DoubleClick/тишина завершили). */ val listening = MutableStateFlow(false) /** Накопленный текст распознавания (для оверлея); обновляется на SttPhrase. */ val sttText = MutableStateFlow("") /** Текст ответа ассистента (обновляется на ShowText); остаётся до новой сессии. */ val assistantText = MutableStateFlow("") /** Текущая Activity (для закрытия приложения по команде с телефона). */ var mainActivity: MainActivity? = null /** Применить и сохранить масштаб. */ fun applyScale(value: Float) { val v = value.coerceIn(0.5f, 1f) scale.value = v prefs.edit().putFloat("scale", v).apply() } override fun onCreate() { super.onCreate() instance = this scale.value = prefs.getFloat("scale", 1f).coerceIn(0.5f, 1f) MercurySDK.init(this) movieController = MovieController(applicationContext) movieController.eventLogger = { msg -> logToFile("mc: $msg") } downloadManager = GlassesDownloadManager(applicationContext) { msg -> hostConnection.sendToHost(msg) } hostConnection = HostConnection( fallbackUrl = GlassesConfig.HOST_URL, movieController = movieController, downloadManager = downloadManager, context = applicationContext, ) hostConnection.start() log("app", "HostConnection стартует: mDNS-поиск, fallback: ${GlassesConfig.HOST_URL}") } companion object { lateinit var instance: GlassesApp private set /** Диагностические события в filesDir/events.log (доступно из любой точки, в т.ч. MovieController). */ fun logToFile(msg: String) { runCatching { val f = File(instance.filesDir, "events.log") f.appendText("${System.currentTimeMillis()} $msg\n") } } } }