diff --git a/app/src/main/java/com/app/assistant/MainActivity.kt b/app/src/main/java/com/app/assistant/MainActivity.kt index 2b70bb6..0b93405 100644 --- a/app/src/main/java/com/app/assistant/MainActivity.kt +++ b/app/src/main/java/com/app/assistant/MainActivity.kt @@ -151,13 +151,18 @@ class MainActivity : ComponentActivity() { } } + var wasHandsFreeActive = false lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.isHandsFreeModeActive.collect { active -> if (active) { + wasHandsFreeActive = true startSpeechRecognition(isHandsFree = true) } else { - speechRecognizerManager.stop() + if (wasHandsFreeActive) { + wasHandsFreeActive = false + speechRecognizerManager.stop() + } } } } diff --git a/app/src/main/java/com/app/assistant/speech/SpeechRecognizerManager.kt b/app/src/main/java/com/app/assistant/speech/SpeechRecognizerManager.kt index a6bf014..84e1243 100644 --- a/app/src/main/java/com/app/assistant/speech/SpeechRecognizerManager.kt +++ b/app/src/main/java/com/app/assistant/speech/SpeechRecognizerManager.kt @@ -161,14 +161,10 @@ class SpeechRecognizerManager( fun preLoadModelAsync() { val mode = settingsRepository.getSttMode() - if (modelManager.isModelDownloaded()) { + if (mode == SttMode.PARAKEET && modelManager.isModelDownloaded()) { scope.launch(Dispatchers.IO) { - if (mode == SttMode.PARAKEET || mode == SttMode.HYBRID || mode == SttMode.NATIVE) { - initVad() - initOfflineRecognizer() - } else if (mode == SttMode.API) { - initVad() - } + initVad() + initOfflineRecognizer() } } } diff --git a/app/src/main/java/com/app/assistant/tts/ApiTtsManager.kt b/app/src/main/java/com/app/assistant/tts/ApiTtsManager.kt index b37faa7..f1b2367 100644 --- a/app/src/main/java/com/app/assistant/tts/ApiTtsManager.kt +++ b/app/src/main/java/com/app/assistant/tts/ApiTtsManager.kt @@ -4,23 +4,43 @@ import android.content.Context import com.app.assistant.config.SpeechConfig import com.app.assistant.config.TtsApiProvider import com.app.assistant.repository.SettingsRepository +import okhttp3.OkHttpClient class ApiTtsManager( private val context: Context, private val settingsRepository: SettingsRepository, + private val okHttpClient: OkHttpClient, private val onSpeakingStateChanged: (isSpeaking: Boolean) -> Unit ) : TtsManager { - private val edgeTtsApiManager = EdgeTtsApiManager(context, settingsRepository, ::handleSpeakingStateChanged) - private val googleTtsApiManager = GoogleTtsApiManager(context, settingsRepository, ::handleSpeakingStateChanged) + private var edgeTtsApiManager: EdgeTtsApiManager? = null + private var googleTtsApiManager: GoogleTtsApiManager? = null + + private fun getEdgeTtsApiManager(): EdgeTtsApiManager { + return edgeTtsApiManager ?: EdgeTtsApiManager( + context, + settingsRepository, + okHttpClient, + ::handleSpeakingStateChanged + ).also { edgeTtsApiManager = it } + } + + private fun getGoogleTtsApiManager(): GoogleTtsApiManager { + return googleTtsApiManager ?: GoogleTtsApiManager( + context, + settingsRepository, + okHttpClient, + ::handleSpeakingStateChanged + ).also { googleTtsApiManager = it } + } private val activeProvider: TtsApiProvider get() = SpeechConfig.ACTIVE_TTS_PROVIDER private val currentManager: TtsManager get() = when (activeProvider) { - TtsApiProvider.EDGE_TTS -> edgeTtsApiManager - TtsApiProvider.GOOGLE_TTS -> googleTtsApiManager + TtsApiProvider.EDGE_TTS -> getEdgeTtsApiManager() + TtsApiProvider.GOOGLE_TTS -> getGoogleTtsApiManager() } private fun handleSpeakingStateChanged(isSpeaking: Boolean) { @@ -30,23 +50,27 @@ class ApiTtsManager( override fun speak(text: String, queueMode: Int) { // Stop the other manager to prevent overlapped audio if provider was changed dynamically when (activeProvider) { - TtsApiProvider.EDGE_TTS -> googleTtsApiManager.stop() - TtsApiProvider.GOOGLE_TTS -> edgeTtsApiManager.stop() + TtsApiProvider.EDGE_TTS -> googleTtsApiManager?.stop() + TtsApiProvider.GOOGLE_TTS -> edgeTtsApiManager?.stop() } currentManager.speak(text, queueMode) } override fun stop() { - edgeTtsApiManager.stop() - googleTtsApiManager.stop() + edgeTtsApiManager?.stop() + googleTtsApiManager?.stop() } override fun isSpeaking(): Boolean { - return currentManager.isSpeaking() + val manager = when (activeProvider) { + TtsApiProvider.EDGE_TTS -> edgeTtsApiManager + TtsApiProvider.GOOGLE_TTS -> googleTtsApiManager + } + return manager?.isSpeaking() ?: false } override fun shutdown() { - edgeTtsApiManager.shutdown() - googleTtsApiManager.shutdown() + edgeTtsApiManager?.shutdown() + googleTtsApiManager?.shutdown() } } diff --git a/app/src/main/java/com/app/assistant/tts/EdgeTtsApiManager.kt b/app/src/main/java/com/app/assistant/tts/EdgeTtsApiManager.kt index c511dd1..f164cb8 100644 --- a/app/src/main/java/com/app/assistant/tts/EdgeTtsApiManager.kt +++ b/app/src/main/java/com/app/assistant/tts/EdgeTtsApiManager.kt @@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap class EdgeTtsApiManager( private val context: Context, private val settingsRepository: SettingsRepository, + okHttpClient: OkHttpClient, private val onSpeakingStateChanged: (isSpeaking: Boolean) -> Unit ) : TtsManager { @@ -43,7 +44,7 @@ class EdgeTtsApiManager( private var playbackJob: Job? = null private var idleTimeoutJob: Job? = null - private val client = OkHttpClient.Builder() + private val client = okHttpClient.newBuilder() .connectTimeout(5, java.util.concurrent.TimeUnit.SECONDS) .readTimeout(5, java.util.concurrent.TimeUnit.SECONDS) .writeTimeout(5, java.util.concurrent.TimeUnit.SECONDS) diff --git a/app/src/main/java/com/app/assistant/tts/GoogleTtsApiManager.kt b/app/src/main/java/com/app/assistant/tts/GoogleTtsApiManager.kt index d3a4584..cbccb1c 100644 --- a/app/src/main/java/com/app/assistant/tts/GoogleTtsApiManager.kt +++ b/app/src/main/java/com/app/assistant/tts/GoogleTtsApiManager.kt @@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap class GoogleTtsApiManager( private val context: Context, private val settingsRepository: SettingsRepository, + okHttpClient: OkHttpClient, private val onSpeakingStateChanged: (isSpeaking: Boolean) -> Unit ) : TtsManager { @@ -44,7 +45,7 @@ class GoogleTtsApiManager( private var generatorJob: Job? = null private var playbackJob: Job? = null - private val client = OkHttpClient.Builder() + private val client = okHttpClient.newBuilder() .connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS) .readTimeout(10, java.util.concurrent.TimeUnit.SECONDS) .writeTimeout(10, java.util.concurrent.TimeUnit.SECONDS) diff --git a/app/src/main/java/com/app/assistant/tts/TtsEngineSelector.kt b/app/src/main/java/com/app/assistant/tts/TtsEngineSelector.kt index 423ba2d..be119be 100644 --- a/app/src/main/java/com/app/assistant/tts/TtsEngineSelector.kt +++ b/app/src/main/java/com/app/assistant/tts/TtsEngineSelector.kt @@ -7,43 +7,56 @@ import java.io.File class TtsEngineSelector( private val context: Context, private val settingsRepository: SettingsRepository, + private val okHttpClient: okhttp3.OkHttpClient = com.app.assistant.viewmodel.MainViewModelFactory.okHttpClient, private val onSpeakingStateChanged: (isSpeaking: Boolean) -> Unit ) : TtsManager { - private val nativeTtsManager = NativeTtsManager(context, onSpeakingStateChanged) - private val offlineTtsManager = OfflineTtsManager(context, settingsRepository, onSpeakingStateChanged) - private val apiTtsManager = ApiTtsManager(context, settingsRepository, onSpeakingStateChanged) + private var nativeTtsManager: NativeTtsManager? = null + private var offlineTtsManager: OfflineTtsManager? = null + private var apiTtsManager: ApiTtsManager? = null + + private fun getNativeTtsManager(): NativeTtsManager { + return nativeTtsManager ?: NativeTtsManager(context, onSpeakingStateChanged).also { nativeTtsManager = it } + } + + private fun getOfflineTtsManager(): OfflineTtsManager { + return offlineTtsManager ?: OfflineTtsManager(context, settingsRepository, onSpeakingStateChanged).also { offlineTtsManager = it } + } + + private fun getApiTtsManager(): ApiTtsManager { + return apiTtsManager ?: ApiTtsManager(context, settingsRepository, okHttpClient, onSpeakingStateChanged).also { apiTtsManager = it } + } override fun speak(text: String, queueMode: Int) { val mode = settingsRepository.getTtsMode() if (queueMode == TtsManager.QUEUE_FLUSH) { - nativeTtsManager.stop() - offlineTtsManager.stop() - apiTtsManager.stop() + nativeTtsManager?.stop() + offlineTtsManager?.stop() + apiTtsManager?.stop() } when (mode) { TtsMode.OFFLINE -> { if (isOfflineModelInstalled()) { - offlineTtsManager.speak(text, queueMode) + getOfflineTtsManager().speak(text, queueMode) } else { - nativeTtsManager.speak(text, queueMode) + getNativeTtsManager().speak(text, queueMode) } } TtsMode.API -> { - apiTtsManager.speak(text, queueMode) + getApiTtsManager().speak(text, queueMode) } else -> { - nativeTtsManager.speak(text, queueMode) + getNativeTtsManager().speak(text, queueMode) } } } override fun stop() { - nativeTtsManager.stop() - offlineTtsManager.stop() - apiTtsManager.stop() + nativeTtsManager?.stop() + offlineTtsManager?.stop() + apiTtsManager?.stop() } override fun isSpeaking(): Boolean { @@ -51,24 +64,24 @@ class TtsEngineSelector( return when (mode) { TtsMode.OFFLINE -> { if (isOfflineModelInstalled()) { - offlineTtsManager.isSpeaking() + offlineTtsManager?.isSpeaking() ?: false } else { - nativeTtsManager.isSpeaking() + nativeTtsManager?.isSpeaking() ?: false } } TtsMode.API -> { - apiTtsManager.isSpeaking() + apiTtsManager?.isSpeaking() ?: false } else -> { - nativeTtsManager.isSpeaking() + nativeTtsManager?.isSpeaking() ?: false } } } override fun shutdown() { - nativeTtsManager.shutdown() - offlineTtsManager.shutdown() - apiTtsManager.shutdown() + nativeTtsManager?.shutdown() + offlineTtsManager?.shutdown() + apiTtsManager?.shutdown() } private fun isOfflineModelInstalled(): Boolean {