Skip to content

Commit cd437d4

Browse files
committed
Changes apllied
1 parent f756f94 commit cd437d4

13 files changed

Lines changed: 1608 additions & 0 deletions

app/src/main/java/com/nlinterface/activities/BarcodeSettingsActivity.kt

Lines changed: 513 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
package com.nlinterface.viewmodels
2+
3+
import android.app.Application
4+
import android.speech.SpeechRecognizer
5+
import android.speech.tts.TextToSpeech
6+
import android.speech.tts.TextToSpeech.OnInitListener
7+
import android.speech.tts.UtteranceProgressListener
8+
import android.util.Log
9+
import androidx.lifecycle.AndroidViewModel
10+
import androidx.lifecycle.LiveData
11+
import androidx.lifecycle.MutableLiveData
12+
import com.nlinterface.utility.STTInputType
13+
import com.nlinterface.utility.SpeechToTextUtility
14+
import com.nlinterface.utility.TextToSpeechUtility
15+
import kotlinx.coroutines.suspendCancellableCoroutine
16+
import java.util.Locale
17+
import kotlin.coroutines.resume
18+
19+
class BarcodeSettingsViewModel (
20+
application: Application
21+
) : AndroidViewModel(application), OnInitListener {
22+
23+
private lateinit var tts: TextToSpeechUtility
24+
private val stt = SpeechToTextUtility()
25+
26+
// holds boolean, whether the TTS system has successfully been initialized.
27+
// private MutableLiveData, so that only the ViewModel has access to the setter
28+
private val _ttsInitialized: MutableLiveData<Boolean> by lazy {
29+
MutableLiveData<Boolean>()
30+
}
31+
32+
// outward LiveData for _ttsInitialized. Immutable, so only access to the getter is given to
33+
// outside classes, and LiveData so that observers can be set.
34+
val ttsInitialized: LiveData<Boolean>
35+
get() = _ttsInitialized
36+
37+
// holds boolean, whether the STT system is currently listening.
38+
private val _isListening: MutableLiveData<Boolean> by lazy {
39+
MutableLiveData<Boolean>()
40+
}
41+
42+
// outward immutable LiveData for _isListening.
43+
val isListening: LiveData<Boolean>
44+
get() = _isListening
45+
46+
// holds the command extracted by the STT system
47+
private val _command: MutableLiveData<String> by lazy {
48+
MutableLiveData<String>()
49+
}
50+
51+
// outward immutable LiveData for _command.
52+
val command: LiveData<String>
53+
get() = _command
54+
55+
// holds the response extracted by the STT system
56+
private val _response: MutableLiveData<String> by lazy {
57+
MutableLiveData<String>()
58+
}
59+
60+
// outward immutable LiveData for _response.
61+
val response: LiveData<String>
62+
get() = _response
63+
64+
/**
65+
* Initializes TTS variable. Must be done here (thus making tts late-init), because context can
66+
* only be accessed locally for a function.
67+
*/
68+
fun initTTS() {
69+
tts = TextToSpeechUtility(getApplication<Application>().applicationContext, this)
70+
}
71+
72+
/**
73+
* Calls the TTS system to read a given string out loud.
74+
*
75+
* @param text: String, the string to be read out loud.
76+
* @param queueMode: enum, defining how multiple speech outputs are queued.
77+
* TextToSpeech.QUEUE_FLUSH (default) overwrites the queue with the current
78+
* text, thus immediately reading it out loud
79+
* TextToSpeech.QUEUE_ADD appends the current text to the queue, only reading
80+
* it once all prior texts have been read out loud
81+
*
82+
* TODO: error handling
83+
*/
84+
fun say(text: String, queueMode: Int = TextToSpeech.QUEUE_FLUSH, utteranceId: String? = "1") {
85+
if (ttsInitialized.value == true) {
86+
tts.say(text, queueMode, utteranceId)
87+
}
88+
}
89+
90+
/**
91+
* Reads a given text out loud and waits until the utterance is completed, before resuming. Is
92+
* required when performing an action immediately after the utterance is completed.
93+
*
94+
* @param text: String, the string to be read out loud.
95+
* @param queueMode: enum, defining how multiple speech outputs are queued.
96+
* TextToSpeech.QUEUE_FLUSH (default) overwrites the queue with the current
97+
* text, thus immediately reading it out loud
98+
* TextToSpeech.QUEUE_ADD appends the current text to the queue, only reading
99+
* it once all prior texts have been read out loud
100+
*
101+
* @param utteranceId: String? identifying the utterance to be made and completed
102+
*/
103+
suspend fun sayAndAwait(
104+
text: String,
105+
queueMode: Int = TextToSpeech.QUEUE_FLUSH,
106+
utteranceId: String? = "1")
107+
= suspendCancellableCoroutine {
108+
tts.setUtteranceProgressListener(object : UtteranceProgressListener() {
109+
110+
override fun onDone(utteranceId: String?) {
111+
Log.println(Log.DEBUG, "onDone", "done")
112+
it.resume(Unit)
113+
}
114+
115+
override fun onStart(utteranceId: String?) {}
116+
override fun onError(utteranceId: String?) {}
117+
118+
})
119+
120+
if (ttsInitialized.value == true) {
121+
tts.say(text, queueMode, utteranceId)
122+
}
123+
}
124+
125+
/**
126+
* Overrides the TextToSpeech.OnInitListener's onInit function. Called, once the TTS engine
127+
* initialization is completed. If initialization was successful, the TTS engine's locale is
128+
* set to the user's phone locale and the speed rate is set to default. Finally, the
129+
* MutableLiveData _ttsInitialized is set to true. If initialization was unsuccessful, an error
130+
* message is printed to the console output.
131+
*
132+
* @param status: Int representing the success status
133+
*
134+
* TODO: improve error handling
135+
*/
136+
override fun onInit(status: Int) {
137+
138+
if (status == TextToSpeech.SUCCESS) {
139+
tts.setLocale(Locale.getDefault())
140+
tts.setSpeedRate()
141+
_ttsInitialized.value = true
142+
} else {
143+
Log.println(Log.ERROR, "tts onInit", "Couldn't initialize TTS Engine")
144+
}
145+
}
146+
147+
/**
148+
* Initializes the STT system, by creating the SpeechRecognizer and setting the
149+
* SpeechRecognitionListener to handle Command functionalities.
150+
*
151+
* TODO: improve error handling
152+
*/
153+
fun initSTT() {
154+
stt.createSpeechRecognizer(getApplication<Application>().applicationContext)
155+
setSTTSpeechRecognitionListener(STTInputType.COMMAND)
156+
}
157+
158+
/**
159+
* Defines the functionality of the SpeechRecognitionListener, aka how to handle STT calls.
160+
* Once results are returned by the STT recognizer, listening is cancelled, matches are
161+
* retrieved and their text output can then be handled by this ViewModel. Listening is also
162+
* cancelled once the speech ends or when an error occurs.
163+
*
164+
* @param inputType: STTInputType, defines whether the voiceInput should be handled as a command
165+
* or as an answer to a system question.
166+
*/
167+
fun setSTTSpeechRecognitionListener(inputType: STTInputType = STTInputType.COMMAND) {
168+
stt.setSpeechRecognitionListener(
169+
onResults = {
170+
cancelSTTListening()
171+
val matches = it.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
172+
if (matches != null && matches.size > 0) {
173+
// results are added in decreasing order of confidence to the list,
174+
// so choose the first one
175+
handleSTTResult(matches[0], inputType)
176+
}
177+
}, onEndOfSpeech = {
178+
cancelSTTListening()
179+
}, onError = {
180+
cancelSTTListening()
181+
})
182+
}
183+
184+
/**
185+
* Handles the processing of the results returned by the STT system, depending on the current
186+
* InputType.
187+
*
188+
* @param s: String, output of the STT system onResults
189+
* @param inputType: STTInputType, defines whether the voiceInput should be handled as a command
190+
* or as an answer to a system question.
191+
*
192+
* TODO: streamline processing and command structure
193+
*/
194+
private fun handleSTTResult(s: String, inputType: STTInputType) {
195+
196+
when (inputType) {
197+
STTInputType.COMMAND -> _command.value = s.lowercase()
198+
STTInputType.ANSWER -> _response.value = s.lowercase()
199+
}
200+
201+
}
202+
203+
/**
204+
* Called when STT begins listening to voice input. Sets _isListening to true and calls on the
205+
* STT system for further handling.
206+
*
207+
* TODO: error handling (What if stt.handleSpeechBegin() is unsuccessful?)
208+
*/
209+
fun handleSTTSpeechBegin() {
210+
stt.handleSpeechBegin()
211+
_isListening.value = true
212+
}
213+
214+
/**
215+
* Called when STT should stop listening. Calls on the STT system for further handling and sets
216+
* _isListening to false
217+
*
218+
* TODO: error handling (What if stt.cancelListening() is unsuccessful?)
219+
*/
220+
fun cancelSTTListening() {
221+
stt.cancelListening()
222+
_isListening.value = false
223+
}
224+
225+
}

0 commit comments

Comments
 (0)