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.
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-backgroundmusicplaylists
extension 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 BackgroundActivated
event 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);
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"
}
]
}
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!