package pw.binom.agentik.android import android.app.Application import androidx.work.BackoffPolicy import androidx.work.Constraints import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import pw.binom.agentik.android.agent.AgentConnection import pw.binom.agentik.android.agent.real.HttpAgentConnection import pw.binom.agentik.android.asr.QwenDownloadWorker import pw.binom.agentik.android.settings.SettingsRepository import java.util.concurrent.TimeUnit /** * Точка входа приложения. Поднимает фоновые сервисы: * - периодический [QwenDownloadWorker]: раз в 6 часов проверяет/докачивает голосовую модель * Qwen3-ASR по Wi-Fi (UNMETERED). Зарегистрирован через [ExistingPeriodicWorkPolicy.KEEP], * чтобы перезапуск приложения не сбрасывал уже запланированный запуск и не дёргал воркер * лишний раз. * - singleton-фабрика [agentConnection] — единая точка подключения к агенту, * живёт до вызова `close()` (никогда в этой PR — приложение короткоживущее). * В фазах тестирования можно подменить на stub: `(application as AgentikApp).agentConnection = StubAgentConnection(...)`. * - [settingsRepository] — JSON-персистентность [pw.binom.agentik.android.settings.Settings] * в `filesDir/settings.json`. Передаётся в UI через [MainActivity]. */ class AgentikApp : Application() { /** * Singleton-фабрика агента-фасада. Создаётся лениво при первом обращении, * живёт до вызова `close()` (никогда в этой PR — приложение короткоживущее). * * NB: используем `lateinit` (а не `by Delegates.notNull()`), чтобы * работал `::agentConnection.isInitialized` — `Delegates.notNull()` * делегат не поддерживает эту проверку. */ lateinit var agentConnection: AgentConnection private set /** * Singleton-репозиторий для JSON-персистентности настроек в `filesDir/settings.json`. * Создаётся лениво при первом обращении (после `onCreate()`) — `Application` * сам передаёт себя как `Context`. */ val settingsRepository: SettingsRepository by lazy { SettingsRepository(this) } override fun onCreate() { super.onCreate() if (!::agentConnection.isInitialized) { agentConnection = HttpAgentConnection(context = this) } scheduleQwenDownload() } private fun scheduleQwenDownload() { val request = PeriodicWorkRequestBuilder(6, TimeUnit.HOURS) .setConstraints( Constraints.Builder() // Wi-Fi only — мобильная сеть не качает 940 МБ. .setRequiredNetworkType(NetworkType.UNMETERED) .build(), ) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.MINUTES) .build() WorkManager.getInstance(this).enqueueUniquePeriodicWork( UNIQUE_WORK_NAME, ExistingPeriodicWorkPolicy.KEEP, request, ) } private companion object { const val UNIQUE_WORK_NAME = "qwen-model-download" } }