0

How to play audio on Android using Xamarin Forms? I have the following service, this works but Forms.Context is deprecated with the message "Context is obsolete as of version 2.5. Please use a local context instead.".

[assembly: Dependency(typeof(AudioService))]
namespace SensaLabScan.Droid.Services
{
    public class AudioService : IAudioService
    {
        private readonly MediaPlayer _mediaPlayer = new MediaPlayer();

        public void PlayBeep()
        {
            _mediaPlayer.Reset();

            // Forms.Context references the Activity which calls Forms.Init, i.e. MainActivity.
            using (var beepFile = Forms.Context.Assets.OpenFd("beep.mp3"))
            {
                _mediaPlayer.SetDataSource(beepFile);
                _mediaPlayer.Prepare();
                _mediaPlayer.Start();
            }
        }
    }
}

What is the alternative to the deprecated Forms.Context in this scenario? I've tried

  1. Android.App.Application.Context.Assets.OpenFd("beep.pm3"); but this raises a file not found exception
  2. Using the MainActivity as the context and using the Assets from that, but again a file not found error.

The mp3 files I'm looking to read are in the Assets folder and marked as AndroidAsset.

Stuart Hallows
  • 8,795
  • 5
  • 45
  • 57
  • Just a thought, Is the context from both `Forms.Context` and `Android.App.Application.Context` are of same type (ApplicationContext or ActivityContext). I had a similar issue with a mix up of these. – Nikhileshwar Jan 21 '20 at 05:57
  • Does this answer your question? [Forms.Context is obsolete so how should I get Activity of my single activity application?](https://stackoverflow.com/questions/51258783/forms-context-is-obsolete-so-how-should-i-get-activity-of-my-single-activity-app) – VahidShir Jan 21 '20 at 06:42
  • @Nikhileshwar Forms Context and Application Context might be of the same type but they are not the same thing!! One has only Activity level context whereas the other holds Application context. Please check my answer for more details – FreakyAli Jan 21 '20 at 07:33

2 Answers2

0

Agree with Nikhileshwar, you should Replace Forms.Context with Android.App.Application.Context

Here is my code to use DependenceService to play the audio.

 [assembly: Dependency(typeof(MyDependenceService))]
 namespace MediaPlayDemo.Droid
 {
public class MyDependenceService : IPlayMedia
{
    public void playMusic()
    {
        //throw new NotImplementedException();


        var bytes = default(byte[]);
        using (StreamReader reader = new StreamReader(Android.App.Application.Context.Assets.Open("Test.mp3")))
        {
            using (var memstream = new MemoryStream())
            {
                reader.BaseStream.CopyTo(memstream);
                bytes = memstream.ToArray();
            }
        }
        Play(bytes);
    }


        //  Stop();

        MediaPlayer currentPlayer;
        public void Play(byte[] AudioFile)
        {
            Stop();
            currentPlayer = new MediaPlayer();
            currentPlayer.Prepared += (sender, e) =>
            {
                currentPlayer.Start();
            };
            currentPlayer.SetDataSource(new StreamMediaDataSource(new System.IO.MemoryStream(AudioFile)));
            currentPlayer.Prepare();
        }

        void Stop()
        {
            if (currentPlayer == null)
                return;

            currentPlayer.Stop();
            currentPlayer.Dispose();
            currentPlayer = null;
        }




}

public class StreamMediaDataSource : MediaDataSource
{
    System.IO.Stream data;

    public StreamMediaDataSource(System.IO.Stream Data)
    {
        data = Data;
    }

    public override long Size
    {

        get
        {
            return data.Length;
        }
    }

    public override int ReadAt(long position, byte[] buffer, int offset, int size)
    {
        data.Seek(position, System.IO.SeekOrigin.Begin);
        return data.Read(buffer, offset, size);
    }

    public override void Close()
    {
        if (data != null)
        {
            data.Dispose();
            data = null;
        }
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);

        if (data != null)
        {
            data.Dispose();
            data = null;
        }
    }
}
}

Here is my demo.

https://github.com/851265601/PlayAudio

Leon
  • 8,404
  • 2
  • 9
  • 52
0

Always make sure which context should be using for what as application context isn't ideal for using everywhere for a good idea to know where to use what check

When to call activity context OR application context?

The easiest way to handle this is to use the CurrentActivity plugin setup process described here: https://github.com/jamesmontemagno/CurrentActivityPlugin

After using that all you have to do is:

 using (var beepFile = CrossCurrentActivity.Current.Activity.Assets.OpenFd("beep.mp3"))
        {
            _mediaPlayer.SetDataSource(beepFile);
            _mediaPlayer.Prepare();
            _mediaPlayer.Start();
        }
FreakyAli
  • 13,349
  • 3
  • 23
  • 63