A while ago, I discovered that playEarcon()
never produces onUtteranceCompleted()
.
At the time I just interpreted the documentation that said "Called when an utterance has been synthesized" as onUtteranceCompleted()
being not applicable for earcons because, an earcon isn't really a result of TTS synthesization.
But looking again Android's source code, I simply can't find an explanation that would justify my interpretation.
A few facts about my test jig:
onUtteranceCompleted()
always arrives for utterance ID preceding the earcon. That utterance is an ordinary TTS utterance, not an earcon.onUtteranceCompleted()
for that earcon never shows up. This is very consistent and reproducible behavior.Delving deep into the TtsService source code, there seem to be only 2 methods that could affect the arrival (or absence) of onUtteranceCompleted()
:
If you examine that code, you will see that a 3rd candidate, TtsService.getSoundResource() is ruled out (as being responsible for the lack of onUtteranceComplete for my earcon) because of fact #2 above: The earcon always plays, hence getSoundResource()
cannot possibly return null.
Using the same logic, the 1st candidate, TtsService.processSpeechQueue(), can also be ruled out, for the same fact #2: The earcon always plays, hence the following 2 critical statements are always executed:
1108 mPlayer.setOnCompletionListener(this);
...
1111 mPlayer.start();
So, we are left with the 2nd candidate only, TtsService.onCompletion(), as a possible explanation for why a playEarcon()
never produces onUtteranceCompleted()
:
public void onCompletion(MediaPlayer arg0) {
// mCurrentSpeechItem may become null if it is stopped at the same
// time it completes.
SpeechItem currentSpeechItemCopy = mCurrentSpeechItem;
if (currentSpeechItemCopy != null) {
String callingApp = currentSpeechItemCopy.mCallingApp;
ArrayList<String> params = currentSpeechItemCopy.mParams;
String utteranceId = "";
if (params != null) {
for (int i = 0; i < params.size() - 1; i = i + 2) {
String param = params.get(i);
if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)) {
utteranceId = params.get(i + 1);
}
}
}
if (utteranceId.length() > 0) {
dispatchUtteranceCompletedCallback(utteranceId, callingApp);
}
}
processSpeechQueue();
}
In there, there are only 2 conditions that would fail to produce dispatchUtteranceCompletedCallback():
But I know for sure that condition #2 can be ruled out because I log all utteranceIds and the earcon's are definitely there.
Also, examining the entire system log:
Log.v(SERVICE_TAG, "TTS callback: dispatch started");
The missing onUtteranceCompleted()
could be the result of dispatchUtteranceCompletedCallback() not being called, but it could also be the result of mCallbacksMap.get(packageName)
returning null.
So, we are left again with 2 possibilities, both of which don't make to me much sense:
mCurrentSpeechItem
is null. But why?
Any suggestions or other explanations for solving this mystery?
Check android.speech.tts.TextToSpeech#playEarcon() at line 807. The params argument passed to the text-to-speech service binder is null, which means the service never receives your utterance ID.
public int playEarcon(String earcon, int queueMode,
HashMap<String,String> params) {
synchronized (mStartLock) {
...
result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
}
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With