I have seen plenty of examples of how to use Android TextToSpeak in an Activity, and have also managed to get this to work just fine. I've also managed to get it to work using a bound service in a plugin, but it seems overcomplicated for my purposes. Here is my VoiceService class:
public class VoiceService : IVoiceService, TextToSpeech.IOnInitListener
{
public event EventHandler FinishedSpeakingEventHandler;
private TextToSpeech _tts;
public void Init()
{
// Use a speech progress listener so we get notified when the service finishes speaking the prompt
var progressListener = new SpeechProgressListener();
progressListener.FinishedSpeakingEventHandler += OnUtteranceCompleted;
//_tts = new TextToSpeech(Application.Context, this);
_tts = new TextToSpeech(Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity, this);
_tts.SetOnUtteranceProgressListener(progressListener);
}
public void OnInit(OperationResult status)
{
// THIS EVENT NEVER FIRES!
Console.WriteLine("VoiceService TextToSpeech Initialised. Status: " + status);
if (status == OperationResult.Success)
{
}
}
public void Speak(string prompt)
{
if (!string.IsNullOrEmpty(prompt))
{
var map = new Dictionary<string, string> { { TextToSpeech.Engine.KeyParamUtteranceId, new Guid().ToString() } };
_tts.Speak(prompt, QueueMode.Flush, map);
Console.WriteLine("tts_Speak: " + prompt);
}
else
{
Console.WriteLine("tts_Speak: PROMPT IS NULL OR EMPTY!");
}
}
/// <summary>
/// When we finish speaking, call the event handler
/// </summary>
public void OnUtteranceCompleted(object sender, EventArgs e)
{
if (FinishedSpeakingEventHandler != null)
{
FinishedSpeakingEventHandler(this, new EventArgs());
}
}
public void Dispose()
{
//throw new NotImplementedException();
}
public IntPtr Handle { get; private set; }
}
Note that the OnInit method never gets called.
In my viewmodel I'd like to do this:
_voiceService.Init();
_voiceService.FinishedSpeakingEventHandler += _voiceService_FinishedSpeakingEventHandler;
... and then later ...
_voiceService.Speak(prompt);
When I do this I get these messages in the output:
10-13 08:13:59.734 I/TextToSpeech( 2298): Sucessfully bound to com.google.android.tts (happens when I create the new TTS object)
and
10-13 08:14:43.924 W/TextToSpeech( 2298): speak failed: not bound to TTS engine (when I call tts.Speak(prompt))
If I was using an activity I would create an intent to get this to work, but I'm unsure how to do that in a plugin.
Thanks in advance,
David
Don't implement Handle
yourself, instead derive from Java.Lang.Object
public class VoiceService : Java.Lang.Object, IVoiceService, TextToSpeech.IOnInitListener
and remove your Dispose()
and Handle
implementation
More info here: http://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/android_callable_wrappers/
Also, I suggest you take an async approach when implementing your service, which would make calling it from view-model something like
await MvxResolve<ITextToSpeechService>().SpeakAsync(text);