How to make NowPlay screen in CarPlay Xamarin.iOS? and How to set nowPlayingIdentifiers property in MPPlayableContentManager?
Asked Answered
B

1

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.

Boehike answered 25/9, 2020 at 6:51 Comment(10)
Did you add a breakpoint and check if the code you write is executed? And debug where the different Title & Subtitle comes from?Bridlewise
Yes, I have check the code, it's executes. And when it execute I got the correct name which I want to send @JackHua-MSFTBoehike
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://mcmap.net/q/1754342/-how-to-show-the-now-playing-tab-in-carplay dance to make it work (at least as of iOS13, no idea whether 14 has changed things again…)Ghastly
@Ghastly how to create audio session? you mean NowPlaying will not work on simulator?Boehike
@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.Ghastly
@Ghastly 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.Boehike
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.Asbestos
Alright, if I'm looking at the right spot, then github.com/Baseflow/XamarinMediaManager/blob/… is going to take care about the audio session.Ghastly
@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.Boehike
@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.Asbestos
B
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;
Boehike answered 21/4, 2021 at 18:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.