Introducing an improved Deezer experience on Xbox: access your favorite songs directly while playing

Until now, users only had access to the Deezer mini player on the Xbox guide. With our latest update, we’ve added a new feature to help our users quickly find the best soundtrack while playing.

A little context

The Xbox Guide

By pressing the Xbox button on your controller, you access the Xbox Guide. This is the quickest way for you to find out what your friends are up to, check your messages and notifications and view your most recently used apps and games.

What is the Enhanced Background Music (EBM) feature?

Our updated application brings improved background music control support. Previously, our player allowed users to access media controls (play, pause, next, previous) and displayed the current track’s cover.

Now, Deezer (since version 4.1.0.70) displays up to 20 additional playlists right under the media controls, making it easier for users to quickly access recently played contents, their music library and their Flow.

During your next game session, open the Xbox guide and select a new playlist to start listening to it!

The magic behind the curtains

We first need to register the EBM feature in the package manifest (which contains the app’s package identity and dependencies, required capabilities, etc). We also need to specify the name of the app service that our application uses to implement the feature. This app service enables the EBM feature to wake up our app in the background and start a direct line of communication with it. App Services can now run in the same process as the foreground application, which makes communication between apps a lot easier to handle.

The Xbox guide without the EBM enabled

Let’s modify our manifest:

<Extensions>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="ms-xbox-backgroundmusicplaylists"
Id="XboxBGMPlaylists"
DisplayName="Playlists"
PublicFolder="PlaylistsFolder">
<uap3:Properties>
<Service>Your_AppService_Name</Service>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appService">
<uap3:AppService Name="Your_AppService_Name" />
</uap3:Extension>
</Extensions>

When the user navigates through Deezer in the Xbox Guide, the EBM feature enumerates apps that are registered to support it (the ms-xbox-backgroundmusicplaylistsextension in our case).

Once a compatible app is found, it will look for the associated service and then start communicating through it, allowing our app to know that actions have been made by a user. Here is the list of actions to be performed:

  • The user wants to access the list of media contents: our service will fetch the user’s data
  • The user selects a playlist: our service will display the tracklist in a second panel
  • The user wants to play a content: our service will start the stream

Implementing the service

When the service is spun up from the Xbox Guide, it triggers a BackgroundActivatedevent that we can catch into our App class:

void App.OnBackgroundActivated(BackgroundActivatedEventArgs args)

This is our entry point to communicate with our App Service.

At this point, we have all the information in the BackgroundActivatedEventArgs:

  • We have the TriggerDetails:
AppServiceTriggerDetails appServiceDetails = args.TaskInstance.TriggerDetails as AppServiceTriggerDetails;
  • We have the deferral (that we saved to keep the communication opened):
_backgroundDeferral = _backgroundInstance.GetDeferral();
  • We also have access to events to manage the state of our service:
_backgroundInstance.Canceled -= OnAppServiceCanceled;
appServiceDetails.AppServiceConnection.ServiceClosed -= OnAppServiceConnectionClosed;
appServiceDetails.AppServiceConnection.RequestReceived -= OnAppServiceRequestReceived;

Handling requests and responses

When our app receives a request, we get a ValueSet of data that we can parse to get the wanted information. Here, we look for a ServiceMethod parameter key.

ValueSet values = args.Request.Message;
if (values.TryGetValue("ServiceMethod", out object method))

After we pull the value out of the ValueSet (within a try/finally block), we can add a switch statement to know what action the user has performed, and what data we need to send back to them.

try
{
switch (method as string)
{
case "GetPlaylists":
// fetch user curated media contents
break;
case "GetPlaylistTracks":
// fetch tracklist content
break;
case "InvokePlaylist":
// start to play the clicked content
break;
}
}

In order to show how the app service should respond to communicate with the Xbox Guide, we will use the GetPlaylists case.

First, we build the content structure we wish to display to the user. There should be 2 different sections as of today:

  • Today, we made for you section (Flow + Favorite Tracks playlist)
  • Recently Played section (Albums and Playlists)

We send back a ValueSet that includes a Result value containing a string of the items in the following JSON format:

{
"PlaylistGroups": [
{
"GroupName": "Recently Played",
"Playlists": [
{
"Title": "Poprock Selector",
"Subtitle": "60 tracks",
"Image": "http://www.uri_to_your_image.jpg",
"Id": "123456",
"ShowDetailsOnInvoke": "true"
}
]
}
]
}

The first three parameters are for display purposes. Id will just be stored to be used later to tell the service what id was invoked.

ShowDetailsOnInvoke is optional, and if provided and true, it will open the next page and ask for playlist track information.

We use the following lines to send the result back:

result.Add("Result", CuratedContent);
AppServiceResponseStatus getPlaylistsStatus = await args.Request.SendResponseAsync(result);
“GetPlaylists” in action

Regarding the GetPlaylistTracks case, when a playlist is marked with ShowDetailsOnInvoke as true, the tracklist is opened in a second panel.

Two more key parameters are associated in the incoming ValueSet. PlaylistId is the clicked playlist whose tracklist we are querying. Similar to the GetPlaylists case, we will return a ValueSet containing a Result string with the following JSON format:

{
"Tracks": [
{
"Title": "Track 1",
"Subtitle": "Artist1",
"Id": "12345"
},
{
"Title": "Track 2",
"Subtitle": "Artist2",
"Id": "6789"
}
]
}
“GetPlaylistTracks” in action

As for the InvokePlaylist case, we need to start playing content. We get another parameter key, PlaylistId, and its value will be the string id provided by GetPlaylists (in case ShowDetailsOnInvoke equals false e.g Flow). If we are in a tracklist details page, there will be a third parameter key, TrackId, which is the selected track in the playlist.
We can then complete the call with an empty ValueSet.

Limitations

There is a whitelist of app-ids that the EBM feature checks for a service to be able to communicate (mostly music streaming apps that are available in the Microsoft Store).

Some feature limitations also exist: a maximum of 99 songs per playlist can be displayed in the second panel and up to 20 different contents are available.

Final words

The user state can change while using the Deezer app (log in, log out, profile switch, etc.) and we wanted to avoid updating our app service with new information each time it does. Moreover, as the app service uses deferrals, updating the service could cause the suspension of the app in background on multiple occasions. So, to avoid those troubles, we made the decision to create an “in-process” app service that allows a direct line of communication.

However, you can definitely create out-of-process background tasks instead if your application architecture allows it. To do so, you would need to add an entry point in your manifest, and implement the IBackgroundTask interface in your app service project.

And I guess there is just one thing left to say: happy gaming and listening!