Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.
tompaana edited this page Nov 28, 2014 · 4 revisions

Note: The primary documentation covering this example application is located in Lumia Developer's Library.


Music Explorer is an example application demonstrating the use of Nokia Music API together with standard Windows Phone 8 audio features to create an immersive music experience. It shows how to take advantage of Nokia Music API features such as searching for artists by name, requesting top artists and new releases, and it also shows how to launch Nokia Music application from within another application to play mix radio or show artist and product information.

Besides utilizing Nokia Music API features, the app integrates with the local music in the device in a number of educated ways. Favourites are ordered according to the most played artist in the device, recommended list is ordered by how many times an artist is found to be similar to your favourites, and you can also play the local songs of your favourite artist.

Getting started

Compatibility

Music Explorer is compatible with Nokia Lumia Windows Phone 8 devices.

Using the prebuilt installation package

Download the XAP file and install it on your device by using the Application Deployment tool that comes with the Windows Phone 8 SDK.

Building the application

Download the application solution source code and open the MusicExplorer.sln file in the Microsoft Visual Studio Express for Windows Phone 8. Follow the instructions in README.md to install required libraries with NuGet Package Manager. Start building and running the application by hitting F5 or selecting Start Debugging from the Debug menu.

Design

The application is visually attractive example demonstrating how to take advantage of the rich Nokia Music API in Windows Phone applications running in Nokia Lumia devices. With Nokia Music API it is possible to request information on artist by name, such as tracks, albums, and singles available for download via Nokia Music. It is also possible to request lists of latest releases and top artists, or even top artists in a certain genre. Genre lists and the radio mix lists are also available through Nokia Music API.

The main panorama of the application contains six items: favourites, recommended, what's new, who's hot, genres, and mixes. Favourites shows 20 most played artists in the device and the artist information is retrieved using Nokia Music API. Recommended shows artists which are found to be similar to your favourites and the list is ordered by how many times an artist is found to be similar to your favourites. What's new and who's hot simply show 10 latest products and 10 top artists in Nokia Music service. Genres and mixes show corresponding lists of available genres and mixes in Nokia Music service. All the lists are country specific, and for this reason locationing services are used to obtain device's current location. The main panorama of the application is shown in full in the picture below.

Full panorama

Nokia Music API can also be used to launch Nokia Music app from the context of a different application. Nokia Music app can be launched for example to artist or product view or it can be launched directly to play a selected radio mix. A note symbol is shown whenever interaction with specific item takes the user into Nokia Music (see for example the list of "what's new" above).

Another main component of the user interface is the artist pivot. Artist pivot can be used to find out what kind of products are available in Nokia Music service for a certain artist. It is impossible to buy products using Nokia Music API and interaction with a product takes user to Nokia Music application where the product can be bought. While in artist pivot, it is also possible to launch artist mix in Nokia Music or to listen to local tracks stored in the device. Below are screenshots of the artist pivot.

Pivot 1  Pivot 2  Pivot 3  Pivot 4  Pivot 5

Implementation

Architecture

Architecture

User interface

User interface of the application consists of MainPage, ArtistPivotPage, TopArtistsForGenrePage, MixesPage, and informational AboutPage, all derived from PhoneApplicationPage. MainPage contains the main panorama featuring favourites, recommended, top artists, latest releases, genres list, and mixes list panorama items. TopArtistsForGenrePage is reached by selecting a genre from the MainPage and it features, as its name suggests, top artists for that particular genre. Correspondingly MixesPage is reached by selecting a mix group from the MainPage and it features the mixes in that particular mix group. ArtistPivotPage is reached by selecting an artist from either MainPage or TopArtistsForGenrePage and its five pivot items feature artist details, tracks, albums, and singles available for download, as well as similar artists for that particular artist.

Windows Phone Location API

Windows Phone Location APIs under the namespace Windows.Devices.Geolocation are used to determine the phone’s current location. To be able to use location services the application must have ID_CAP_LOCATION capability specified in WMAppManifest.xml file.

Maps API

Windows Phone 8 Maps APIs offer map related services (Microsoft.Phone.Maps.Services). ReverseGeocodeQuery is used to obtain a location specific country code for initializing Nokia Music connection to current location. To be able to use the Maps APIs the application must have ID_CAP_MAP capability specified in WMAppManifest.xml file.

Nokia Music API

Nokia Music API is used to get locale-dependent and up-to-date information on most popular artists and new releases, available genres and mix lists. Searches for artists by name are made in order to link locally stored tracks in the device correctly to the artists available in Nokia Music service. The API is also used to launch Nokia Music application into various states (artist, product, mix) directly from Music Explorer.

XNA Media API

Media APIs in namespace Microsoft.Xna.Framework.Media, especially classes MediaLibrary and MediaPlayer, are used to get a list of tracks stored locally in device with artist and play count information, as well as to shuffle and play locally available tracks of an artist in ArtistPivotPage. These features require ID_CAP_MEDIALIB_AUDIO and ID_CAP_MEDIALIB_PLAYBACK capabilities to be specified in WMAppManifest.xml.

Using Nokia Music

Prerequisites

In order to use Nokia Music API, application must have an unique app ID and app token (unless using launchers only). These can be obtained by visiting API registration page and requesting credentials for Nokia Music API. WP8 application must also add reference to the actual Nokia Music API client, which in turn requires a reference to JSON.Net library to be added to the application. Instructions to add the references to the solution can be found from README.md in the project source.

Localizing the API

Before making any requests to Nokia Music API, Music Explorer checks for Nokia Music availability in current location, resolved using Windows Phone Location API . By default, Nokia Music API uses (and checks) phone Region settings with each API call made. This allows users to see the relevant, and partly localized, country specific information. This can be seen in the image of main panorama on the top of this wiki page, showing genres and mixes available in Finland, as well as Finland's most popular artists at the moment. These lists would contain different items if another valid country code (such as de for Germany or gb for United Kingdom) was used to initialize the Nokia Music API.

using Nokia.Music.Phone;
...

namespace MusicExplorer
{
    ...

    public partial class MainPage : PhoneApplicationPage
    {
        ...

        private CountryResolver resolver = null;
        ...

        private void ReverseGeocodeQuery_QueryCompleted(
            object sender, 
            QueryCompletedEventArgs<IList<MapLocation>> e)
        {
            if (e.Error == null)
            {
                if (e.Result.Count > 0)
                {
                    MapAddress address = e.Result[0].Information.Address;
                    string twoLetterCountryCode = 
                        CountryCodes.TwoLetterFromThreeLetter(address.CountryCode);
                    InitializeNokiaMusicApi(twoLetterCountryCode);
                }
            }
        }
        ...

        private void InitializeNokiaMusicApi(string twoLetterCountryCode)
        {
            if (resolver == null)
            {
                resolver = new CountryResolver(MusicApi.MUSIC_EXPLORER_APP_ID,
                                               MusicApi.MUSIC_EXPLORER_APP_TOKEN);
            }

            resolver.CheckAvailability((Response<bool> response) =>
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    if (response.Result)
                    {
                        // Make initial requests to fill models.
                        App.MusicApi.Initialize(twoLetterCountryCode);
                        ...
                    }
                    else
                    {
                        MessageBox.Show("Sorry, Nokia Music is not available in this locale.");
                    }
                });
            },
            twoLetterCountryCode.ToLower());
        }
    }
}

Creating the client

In Music Explorer application the communication with Nokia Music API, with the exception of CountryResolver usage described above, is encapsulated into MusicApi class for easy reference. All requests to Nokia Music service are made using MusicClient class, and therefore the next step after confirming Nokia Music availability is creating MusicClient in Initialize method of MusicApi.

using Nokia.Music.Phone;
using Nokia.Music.Phone.Tasks;
using Nokia.Music.Phone.Types;
...

namespace MusicExplorer
{
    ...

    public class MusicApi
    {
        // Constants
        public const string MUSIC_EXPLORER_APP_ID = "music_explorer_private_app_id"; // real app id not shown here
        public const string MUSIC_EXPLORER_APP_TOKEN = "music_explorer_private_app_token"; // real app token not shown here

        // Members
        private MusicClient client = null;
        private bool initialized = false;
        ...

        public void Initialize(string countryCode)
        {
            // Create a music client with correct AppId and Token/AppCode
            if (countryCode ### null || countryCode.Length != 2)
            {
                client = new MusicClient(MUSIC_EXPLORER_APP_ID, 
                                         MUSIC_EXPLORER_APP_TOKEN);
            }
            else
            {
                client = new MusicClient(MUSIC_EXPLORER_APP_ID, 
                                         MUSIC_EXPLORER_APP_TOKEN, 
                                         countryCode.ToLower());
            }
            initialized = true;
        }
        ...
    }
}

Making requests

Requests to Nokia Music service can be sent after creating a localized MusicClient instance. For example the GetTopArtists method of MusicApi requests 10 most popular artists of Nokia Music service (in the country the MusicClient was initialized with) and populates the TopArtists of the MainViewModel accordingly. As the process with other requests (new releases, genres, etc.) is almost identical to requesting 10 most popular artists, they are not described in the wiki. See the project source for exact implementation reference.

namespace MusicExplorer
{
    ...

    public class MusicApi
    {
        ...

        // Members
        private MusicClient client = null;
        private bool initialized = false;
        ...

        public void GetTopArtists()
        {
            if (!initialized)
            {
                return;
            }

            client.GetTopArtists((ListResponse<Artist> response) =>
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    // Use results
                    if (response != null && response.Result != null && response.Result.Count > 0)
                    {
                        App.ViewModel.TopArtists.Clear();

                        foreach (Artist a in response.Result)
                        {
                            if (a.Thumb100Uri != null)
                            {
                                App.ViewModel.TopArtists.Add(new ArtistModel() 
                                    { 
                                        Name = a.Name,
                                        Country = CountryCodes.CountryNameFromTwoLetter(a.Country), 
                                        Genres = a.Genres[0].Name, 
                                        ThumbUri = a.Thumb100Uri, 
                                        Id = a.Id
                                    });
                            }
                            else
                            {
                                App.ViewModel.TopArtists.Add(new ArtistModel() 
                                    { 
                                        Name = a.Name,
                                        Country = CountryCodes.CountryNameFromTwoLetter(a.Country), 
                                        Genres = a.Genres[0].Name, 
                                        ThumbUri = new Uri("/Assets/thumb_100_placeholder.png", 
                                                           UriKind.Relative), 
                                        Id = a.Id
                                    });
                            }
                        }
                    }

                    if (response != null && response.Error != null)
                    {
                        ShowNokiaMusicApiError();
                    }
                    HideProgressIndicator("GetTopArtists()");
                });
            });
            ShowProgressIndicator("GetTopArtists()");
        }
        ...
    }
}

The top artists can then be shown in panorama on MainPage (data context is set in the constructor of the MainPage):

namespace MusicExplorer
{
    ...

    public partial class MainPage : PhoneApplicationPage
    {
        ...

        public MainPage()
        {
            ...
            DataContext = App.ViewModel;
            ...
        }
        
        ...
    }
} 
<phone:PhoneApplicationPage
    x:Class="MusicExplorer.MainPage"
    ...>
 
    ...

    <Grid x:Name="LayoutRoot" Background="Transparent">

        <!--Panorama control-->
        <phone:Panorama SelectionChanged="Panorama_SelectionChanged">

            <!--Who's Hot Item-->
            <phone:PanoramaItem>
                <phone:LongListSelector x:Name="TopArtistsList" Margin="0,-38,-22,2" 
                                        ItemsSource="{Binding TopArtists}" 
                                        SelectionChanged="OnTopArtistsSelectionChanged" 
                                        toolkit:TiltEffect.IsTiltEnabled="True">
                    <phone:LongListSelector.ListHeaderTemplate>
                        <DataTemplate>
                            <Grid Margin="12,0,0,20">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <TextBlock Text="who's hot"
                                           Style="{StaticResource PanoramaItemHeaderTextStyle}"
                                           FontSize="65"
                                           Grid.Row="0"/>
                            </Grid>
                        </DataTemplate>
                    </phone:LongListSelector.ListHeaderTemplate>
                    <phone:LongListSelector.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal" Margin="12,2,0,4" Height="105" Width="432">
                                <Image Width="100" Height="100" Source="{Binding ThumbUri}"/>
                                <StackPanel Width="311" Margin="8,-7,0,0">
                                    <TextBlock Text="{Binding Name}" Margin="10,0" 
                                               Style="{StaticResource PhoneTextExtraLargeStyle}"
                                               FontSize="{StaticResource PhoneFontSizeLarge}" />
                                    <TextBlock Text="{Binding Country}" Margin="10,-2,10,0" 
                                               Style="{StaticResource PhoneTextSubtleStyle}" />
                                    <TextBlock Text="{Binding Genres}" Margin="10,-2,10,0" 
                                               Style="{StaticResource PhoneTextSubtleStyle}" />
                                </StackPanel>
                            </StackPanel>
                        </DataTemplate>
                    </phone:LongListSelector.ItemTemplate>
                </phone:LongListSelector>
            </phone:PanoramaItem>
            ...

        </phone:Panorama>
        
    </Grid>
    ...
    
</phone:PhoneApplicationPage>

Nokia Music launchers

Following methods from MusicApi show how simple it is to launch Nokia Music application into mix, product, and artist states using Nokia Music API:

using Nokia.Music.Phone;
using Nokia.Music.Phone.Tasks;
...

namespace MusicExplorer
{
    ...

    public class MusicApi
    {
        ...

        public void LaunchMix(string id)
        {
            ...

            PlayMixTask task = new PlayMixTask();
            task.MixId = id;
            task.Show();
        }

        public void LaunchArtistMix(string artistName)
        {
            ...

            PlayMixTask task = new PlayMixTask();
            task.ArtistName = artistName;
            task.Show();
        }

        public void LaunchProduct(string id)
        {
            ...

            ShowProductTask task = new ShowProductTask();
            task.ProductId = id;
            task.Show();
        }

        public void LaunchArtist(string id)
        {
            ...

            ShowArtistTask task = new ShowArtistTask();
            task.ArtistId = id;
            task.Show();
        }
        ...
    }
}

Building favourites list

The list of artists in MainPage's Favourites view is made in LoadData method of MainViewModel. Comments in the code describe the steps taken to build the list:

using Microsoft.Xna.Framework.Media;
...

namespace MusicExplorer.Models
{
    ...

    public class MainViewModel : INotifyPropertyChanged
    {
        ...

        public ObservableCollection<ArtistModel> LocalAudio { get; private set; }
        ...

        MediaLibrary mediaLib = null; // For accessing local artists and songs.
        ...

        public MainViewModel()
        {
            LocalAudio = new ObservableCollection<ArtistModel>();
            ...

            // Insert a place holder for title text
            LocalAudio.Add(new ArtistModel() { 
                Name = "MusicExplorerTitlePlaceholder", 
                ItemHeight = "110", 
                ItemWidth = "400" 
            });
        }

        ...

        public void LoadData()
        {
            mediaLib = new MediaLibrary();
            ...

            foreach (Artist a in mediaLib.Artists)
            {
                if (a.Songs.Count <= 0) continue; // Skip artists without tracks
                string artist = a.Name;
                int trackCount = a.Songs.Count;
                int playCount = 0;

                // Check the play count of artist's tracks
                foreach (Song s in a.Songs)
                {
                    playCount += s.PlayCount;
                }

                // Insert artist before less played artists..
                bool artistAdded = false;
                for (int i = 1; i < LocalAudio.Count; i++) // Index 0 reserved for title item
                {
                    if (Convert.ToInt16(LocalAudio[i].PlayCount) < playCount)
                    {
                        this.LocalAudio.Insert(i, new ArtistModel()
                        { 
                            Name = artist, 
                            LocalTrackCount = Convert.ToString(trackCount), 
                            PlayCount = Convert.ToString(playCount) 
                        });
                        artistAdded = true;
                        break;
                    }
                }

                // ...Or add artist to the end of the list if it's least played
                if (artistAdded ### false)
                {
                    this.LocalAudio.Add(new ArtistModel() 
                    { 
                        Name = artist, 
                        LocalTrackCount = Convert.ToString(trackCount), 
                        PlayCount = Convert.ToString(playCount) 
                    });
                }
                ...
            }

            // Continue with only the top 20 favourite artists
            int removeIndex = App.ViewModel.LocalAudio.Count - 1;
            while (removeIndex > 20)
            {
                App.ViewModel.LocalAudio.RemoveAt(removeIndex);
                removeIndex--;
            }

            // Divide local artists into two "size categories"
            foreach (ArtistModel m in App.ViewModel.LocalAudio)
            {
                if (m.Name ### "MusicExplorerTitlePlaceholder") continue;
                if (Convert.ToInt16(m.LocalTrackCount) > (totalTrackCount / totalArtistCount))
                {
                    m.ItemHeight = "200";
                    m.ItemWidth = "206";
                }
                else
                {
                    m.ItemHeight = "100";
                    m.ItemWidth = "206";
                }
            }
            ...
        }
        ...
    }
}

And to show the list of favourites on MainPage, the MainPage.xaml needs the following:

<phone:PhoneApplicationPage
    x:Class="MusicExplorer.MainPage"
    ...>

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">

        <!--Panorama control-->
        <phone:Panorama SelectionChanged="Panorama_SelectionChanged">
            ...

            <!--Favourites Item-->
            <phone:PanoramaItem>
                <Grid>
                    <TextBlock Margin="12,68,0,0" FontSize="24" TextWrapping="Wrap" 
                               Text="No local songs available for creating favourites list. Favourites list shows 20 artists with the most played songs." 
                               HorizontalAlignment="Center" Visibility="{Binding NoFavouritesVisibility}"></TextBlock>
                    <ListBox Margin="0,-38, -22,2" x:Name="LocalAudioList" ItemsSource="{Binding LocalAudio}" SelectionChanged="OnFavoriteSelectionChanged">
                        <ListBox.ItemsPanel>
                            <ItemsPanelTemplate>
                                <toolkit:WrapPanel/>
                            </ItemsPanelTemplate>
                        </ListBox.ItemsPanel>

                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <controls:FlipableItem Height="{Binding ItemHeight}"
                                                   Width="{Binding ItemWidth}"
                                                   ItemWidth="{Binding ItemWidth}"
                                                   FrontPrimaryText="{Binding Name}" 
                                                   FrontSecondaryText="{Binding LocalTrackCount}"
                                                   BackImage="{Binding ThumbUri}"/>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </Grid>
            </phone:PanoramaItem>
            ...

        </phone:Panorama>
        
    </Grid>
    ...

</phone:PhoneApplicationPage>

Further reading

See the article Nokia Music API for more in-depth information on the use of Nokia Music API.

Clone this wiki locally