2

I have created Audio App in Xamarin.Forms, for playing audio I have used MediaManager plugin.

Now I want to make it compatible with CarPlay.

CarPlay audio apps are controlled by the MPPlayableContentManager. You are required to implement the MPPlayableContentDelegate and MPPlayableContentDatasource protocol in order to connect with CarPlay. The UI is controlled by CarPlay - all you need to do is feed it data for tabs+tables (datasource) and respond to playable items (delegate).

I have used all required CarPlay api for audio app but, the problem is:

  • Not getting now playing screen in CarPlay simulator.
  • How to set ArtWork in MPContentItem ?

MPPlayableContentDelegate class

public class PlayableContentDelegate : MPPlayableContentDelegate
{
    public override void PlayableContentManager(MPPlayableContentManager contentManager, NSIndexPath indexPath, Action<NSError> completionHandler)
    {
        DispatchQueue.MainQueue.DispatchAsync(() =>
        {
            UIApplication.SharedApplication.BeginReceivingRemoteControlEvents();
            completionHandler(null);
            UIApplication.SharedApplication.EndReceivingRemoteControlEvents();

            UIApplication.SharedApplication.BeginReceivingRemoteControlEvents();
        });
    }

    [Export("playableContentManager:initiatePlaybackOfContentItemAtIndexPath:completionHandler:")]
    public override void InitiatePlaybackOfContentItem(MPPlayableContentManager contentManager, NSIndexPath indexPath, Action<NSError> completionHandler)
    {
        try
        {
            DispatchQueue.MainQueue.DispatchAsync(() =>
            {
                UIApplication.SharedApplication.BeginReceivingRemoteControlEvents();

                var itemToPlay = BaseSettingsService.CurrentPlayList[indexPath.Row];
                var NowPlayingInfoCenter = MPNowPlayingInfoCenter.DefaultCenter;

                MPNowPlayingInfo playingInfo = new MPNowPlayingInfo();
                playingInfo.Title = itemToPlay.Title;
                playingInfo.Artist = itemToPlay.Editor;
                playingInfo.AlbumTitle = "1989";
                playingInfo.Genre = "Pop";
                playingInfo.PlaybackDuration = 231;
                playingInfo.PlaybackRate = 22;
                playingInfo.PersistentID = (ulong)111111;
                playingInfo.PlaybackQueueIndex = 3;
                playingInfo.PlaybackQueueCount = BaseSettingsService.CurrentPlayList.Count;
                playingInfo.IsLiveStream = false;
                playingInfo.MediaType = MPNowPlayingInfoMediaType.Audio;
                NowPlayingInfoCenter.NowPlaying = playingInfo;

                var id = itemToPlay.PodcastId.ToString();
                string[] s1 = new string[1];
                s1[0] = id;

                contentManager.NowPlayingIdentifiers = s1;

                completionHandler(null);

                UIApplication.SharedApplication.EndReceivingRemoteControlEvents();
            });
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }

    public override nuint RetainCount { get; }

    public override void ContextUpdated(MPPlayableContentManager contentManager, MPPlayableContentManagerContext context)
    {
        try
        {
            //base.ContextUpdated(contentManager, context);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }

    public override NSDictionary GetDictionaryOfValuesFromKeys(NSString[] keys)
    {
        return base.GetDictionaryOfValuesFromKeys(keys);
    }
}

MPPlayableContentDataSource

public class AppDelegateDataSource : MPPlayableContentDataSource
{
    public override MPContentItem ContentItem(NSIndexPath indexPath)
    {
        if (indexPath.Length == 1)
        {
            var item = new MPContentItem("PlayList");
            item.Title = "PlayList";
            item.Subtitle = "Hello";
            item.Container = true;
            item.Playable = false;
            return item;
        }
        else
        {
            var play = CurrentPlayList[indexPath.Row];
            var item = new MPContentItem(play.PodcastId);
            item.Title = play.Title;
            item.Subtitle = play.Editor;
            item.Playable = true;
            return item;
        }
    }

    public override nint NumberOfChildItems(NSIndexPath indexPath)
    {
        if (indexPath.GetIndexes().Length == 0)
            return 1;
        else
            return CurrentPlayList.Count;
    }
}

So, the question is How should I respond to playable items now?

Anyone know what I'm missing or which mistake I have to correct? Any help would be appreciated, thanks.

Divyesh
  • 2,085
  • 20
  • 35
  • Did you add a breakpoint and check if the code you write is executed? And debug where the different Title & Subtitle comes from? – nevermore Sep 25 '20 at 09:43
  • Yes, I have check the code, it's executes. And when it execute I got the correct name which I want to send @JackHua-MSFT – Divyesh Sep 25 '20 at 10:31
  • 1.) In how far "different"? Please provide details of what you expect and what you see. 2.) Did you create an audio session and set it active at some point? The NowPlaying screen is unreliable in the simulator, you need the https://stackoverflow.com/a/54625709/415982 dance to make it work (at least as of iOS13, no idea whether 14 has changed things again…) – DrMickeyLauer Sep 25 '20 at 11:27
  • @DrMickeyLauer how to create audio session? you mean NowPlaying will not work on simulator? – Divyesh Sep 25 '20 at 12:09
  • @Divyesh_08 I'm not saying it won't work, but it may require some strange fiddling with calling `beginReceivingRemoteControlEvents()` and `endReceivingRemoteControlEvents()`. As for the audio session, you need to get the current `AVAudioSession.sharedInstance()` and then at least set a configuration (`setConfiguration()` and set it active (`setActive()`), otherwise iOS will not pass any audio through. – DrMickeyLauer Sep 26 '20 at 09:46
  • @DrMickeyLauer thank you for the information. But I have not used AVAudio, I am working with the MediaManager Plugin of Xamarin.Forms. But again thank you. – Divyesh Sep 26 '20 at 11:16
  • You have received CarPlay entitlements??? As far as I am aware those are granted only to very, very big companies, so I highly doubt. If you haven't then... even if it works on the simulator it won't work on the real device. – Ivan Ičin Sep 26 '20 at 13:55
  • Alright, if I'm looking at the right spot, then https://github.com/Baseflow/XamarinMediaManager/blob/c7ef228247198072e4ce6122ac73c6b0ce98e300/MediaManager/Platforms/Ios/Player/IosMediaPlayer.cs#L89 is going to take care about the audio session. – DrMickeyLauer Sep 26 '20 at 16:37
  • @IvanIčin I have not yet done the Entitlement request process yet, but I hope that once I do that process will get the positive response from apple. – Divyesh Sep 26 '20 at 17:44
  • @Divyesh_08 just for your information, I have written three times and never got any response, positive or negative. From other support I’ve got confirmation that’s how it works. So, start with the entitlement first. – Ivan Ičin Sep 26 '20 at 17:49

1 Answers1

0

To get the NowPlaying screen you have to set two things without fail.

  1. MPRemoteCommandCenter
var commandCenter = MPRemoteCommandCenter.Shared;
commandCenter.PlayCommand.Enabled = true;
commandCenter.StopCommand.Enabled = true;
  1. MPPlayableContentManager.NowPlayingIdentifiers
var urlId = "11111";
string[] identifier = new string[1];
identifier[0] = urlId;

contentManager = MPPlayableContentManager.Shared;
contentManager.NowPlayingIdentifiers = identifier;
Divyesh
  • 2,085
  • 20
  • 35