n32303
n32303

Reputation: 829

How to avoid async methods windows phone 8.1

Im creating an windows phone 8.1 app. When app is started, app prompts user to call certain telephone number. It does this with voice. After instructions are told by app, phone call dialog is showed. This is the code:

public MainPage()
    {
        this.InitializeComponent();

        this.NavigationCacheMode = NavigationCacheMode.Required;
        StartSpeaking("Please call number !");

        CallDialog();
    }

    private async void StartSpeaking(string text)
    {

        MediaElement mediaElement = this.media;

        // The object for controlling the speech synthesis engine (voice).
        var synth = new Windows.Media.SpeechSynthesis.SpeechSynthesizer();

        // Generate the audio stream from plain text.
        SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);

        // Send the stream to the media object.
         mediaElement.SetSource(stream, stream.ContentType);
        mediaElement.Play();



    }

 private async void CallDialog()
    {

        Windows.ApplicationModel.Calls.PhoneCallManager.ShowPhoneCallUI("123", "123");
        var messageDialog = new Windows.UI.Popups.MessageDialog("call ended", "Text spoken");
        await messageDialog.ShowAsync();
    }

The problem is that I must use synth.SynthesizeTextToStreamAsync method which is async method so call dialog shows up before text is said. How can I avoid that?

Upvotes: 1

Views: 2138

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456887

async Task methods should be embraced; it is only async void methods that should be avoided (they should only be used as event handlers). I have an MSDN article that describes a few reasons to avoid async void.

In your case, you can use an async void event handler (e.g., for the Loaded event), and make your methods async Task instead of async void and await them:

async void MainPage_Loaded(..)
{
  await StartSpeakingAsync("Please call number !");
  await CallDialogAsync();
}

private async Task StartSpeakingAsync(string text);
private async Task CallDialogAsync();

Update

To (asynchronously) wait for the media to play, you need to hook into an event that notifies you it's complete. MediaEnded looks like a good choice. Something like this should work:

public static Task PlayToEndAsync(this MediaElement @this)
{
  var tcs = new TaskCompletionSource<object>();
  RoutedEventHandler subscription = null;
  subscription = (_, __) =>
  {
    @this.MediaEnded -= subscription;
    tcs.TrySetResult(null);
  };
  @this.MediaEnded += subscription;
  @this.Play();
  return tcs.Task;
}

That method extends the MediaElement with an async-ready PlayToEndAsync method, which you can use like this:

private async Task SpeakAsync(string text)
{
  MediaElement mediaElement = this.media;
  var synth = new Windows.Media.SpeechSynthesis.SpeechSynthesizer();
  SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
  mediaElement.SetSource(stream, stream.ContentType);
  await mediaElement.PlayToEndAsync();
}

Upvotes: 2

Related Questions