Universal Windows 10 Platform (UWP) Sound Cloud Music App for Windows Phone & Raspberry Pi2

Posted by: Shoban Kumar , on 12/21/2015, in Category Windows Store Apps
Views: 16701
Abstract: Create a Universal Windows Platform (UWP) Music Player app and deploy it to both a Windows Phone and Raspberry Pi2 without any code changes.

Windows 10 introduces an entirely new way of developing apps targeting multiple devices and it is called UWP apps or Universal Windows Platform apps. Instead of creating separate apps for different platforms, UWP apps run across all major platforms with minor code changes.

 

This article covers part 2 of our journey to develop a Windows 10 Universal App (UWP) that will run on most of the devices without any code change. In Part 1 http://bit.ly/dnc-win10-soundcloud-1 , I wrote how the new Windows 10 controls allow us to target a wide variety of Windows devices with minimal code. With Adaptive Triggers and Custom App shell, we developed a simple Sound Cloud player that adapts the UI based on the size of the screen.

In this article, we will improve our app by adding more features and changing existing code to support Background Audio and Media Transport Controls. We will also deploy our app to both a Windows Phone and Raspberry Pi2 without any code changes.

This article is published from the DNC Magazine for .NET Developers and Architects. Download this magazine from here [Zip PDF] or Subscribe to this magazine for FREE and download all previous and current editions

Disclaimer: This article contains a lot of code which contains comments wherever needed explaining what the code does.

Project Setup

The previous sample played the first track from the “likes” playlist from SoundCloud but it did not support Background Audio and had the following limitations:

1. Minimizing App stopped the play back

2. Default Media Controls (Keyboard, Screen etc) were hidden

3. Media Controls did not show Song, Album art etc

We will add Background Audio support to our App and fix the above limitations. For Windows 10 development, it’s much easier to enable Background Audio and Manage playlist. To read more about the details of the implementation, read Background Audio (http://bit.ly/1H9mf9Y) MSDN documentation. To make it simpler, we will use the sample Background Audio App provided by Microsoft which can be downloaded from GitHub (http://bit.ly/1NzKtPv) .

Let us perform the following steps. I am assuming you have downloaded the source code of the previous article as well as Microsoft’s Github project.

- Add the BackgroundAudioShared and BackgroundAudioTask projects from the sample project above to our solution, and add references to these projects in MusicPlayer App. Your Solution Explorer should look like the following:

solutionexplorer

- Open the Package Manifest file for MusicPlayer project and Navigate to declarations tab. Add the Background Tasks declaration and set BackgroundAudioTask.MyBackgroundAudioTask as the Entry Point value. Make sure only “Audio” is checked under Supported Task Types.

declaration

- Move SoundCloudUser.cs , SoundCloudTrack.cs and SoundCloudCreatedWith.cs from MusicPlayer project to BackgroundAudioShared project and update the namespace names. Your solution explorer should now look like the following:

solutionexplorer-2

MusicPlayer

- Make the following changes to App.xaml.cs to declare a new SoundCloudUser object which we will use later to display profile details.

public static string SoundCloudClientId = "<YOUR CLIENT ID>";
public static int SCUserID = 0;
public static string SoundCloudLink = "http://api.soundcloud.com/";
public static string SoundCloudAPIUsers = "users/";
public static List<SoundCloudTrack> likes = new List<SoundCloudTrack>();
public static int nowplayingTrackId = 0;
public static SoundCloudUser SCUser { get; set; }

- Make the following code changes to AppShell.xaml.cs to change menu items

// Declare the top level nav items
private List<NavMenuItem> navlist = new List<NavMenuItem>(new[]
{
    new NavMenuItem()
    {
        Symbol = Symbol.Play,
        Label ="Now Playing",
        DestPage = typeof(NowPlaying)
    },
    new NavMenuItem()
    {
        Symbol = Symbol.Emoji2,
        Label = "Likes",
        DestPage = typeof(Likes)
    },
   new NavMenuItem()
    {
        Symbol = Symbol.Contact,
        Label = "Me",
        DestPage = typeof(Me)
    }

});

- Add a new Blank Page to the project and name it Likes.xaml

- Add the following code to Likes.xaml

<Page
    x:Class="MusicPlayer.Likes"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MusicPlayer"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:data="using:BackgroundAudioShared">
    <Page.Resources>
        <DataTemplate x:Key="ImageOverlayTemplate" x:DataType="data:SoundCloudTrack">
            <StackPanel Height="130" Width="190" Margin="4,4,4,8">
                <TextBlock Text="{x:Bind title}" 
                       Margin="8,4" Width="186" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Left"/>
                <Image Source="{x:Bind AlbumArtUri}" Margin="8,0,8,8" Stretch="UniformToFill"/>
            </StackPanel>
        </DataTemplate>
    </Page.Resources>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <GridView x:Name="grdLikes" ItemTemplate="{StaticResource              ImageOverlayTemplate }" IsItemClickEnabled="True" IsSwipeEnabled="False"     CanDragItems="False" SelectionMode="None"  ItemClick="grdLikes_ItemClick" />
    </Grid>
</Page>

In the above code, we have added a GridView control and a Data template which will be used to display a list of all the Tracks added to our “Likes” playlist.

- Add the following code to likes.xaml.cs. I have deliberately removed the namespaces to save space but you can always look up the source for the same

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace MusicPlayer
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class Likes : Page
    {
        public Likes()
        {
            this.InitializeComponent();
            this.Loaded += Likes_Loaded;
        }

        private void Likes_Loaded(object sender, RoutedEventArgs e)
        {
            grdLikes.ItemsSource = App.likes;
        }

        private void grdLikes_ItemClick(object sender, ItemClickEventArgs e)
        {
          var song =  e.ClickedItem as SoundCloudTrack;
          MessageService.SendMessageToBackground(new TrackChangedMessage(new Uri(song.stream_url)));
            
        }

        private void grdLikes_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }
    }
}

In this code, we have done the following:

1. Set the ItemsSource property to global App.likes property. This will populate the Gridview with the list of tracks.

2. When an Item (Track) is clicked in the GridView, we also load the Track details and Send a Message to our Background Task which will control the media playback.

- Make the following code changes to MainPage.xaml.cs to populate the SoundCloudUser object which is declared in App.xaml.cs

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace MusicPlayer
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        this.Loaded += MainPage_Loaded;
    }

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        //Get User
        GetUserDetails();
    }

    private async void GetUserDetails()
    {
        try
        {
            string responseText = await GetjsonStream(App.SoundCloudLink + App.SoundCloudAPIUsers + "shoban-kumar" + ".json?client_id=" + App.SoundCloudClientId);
            App.SCUser = JsonConvert.DeserializeObject<SoundCloudUser>(responseText);

            App.SCUserID = App.SCUser.id;

            //Get Likes 
            GetLikes();
        }
        catch (Exception ex)
        {
            MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
            await showMessgae.ShowAsync();
        }
    }

    private async void GetLikes()
    {

        try
        {

            string responseText = await GetjsonStream(App.SoundCloudLink + App.SoundCloudAPIUsers + App.SCUserID + "/favorites.json?client_id=" + App.SoundCloudClientId);
            App.likes = JsonConvert.DeserializeObject<List<SoundCloudTrack>>(responseText);

            //remove songs which do not have stream url
            App.likes = App.likes.Where(t => t.stream_url != null).ToList<SoundCloudTrack>();

            //add "?client_id=" + App.SoundCloudClientId to stream url
            App.likes = App.likes.Select(t => { t.stream_url += "?client_id=" + App.SoundCloudClientId; return t; }).ToList<SoundCloudTrack>();
            loginProgress.IsActive = false;

            AppShell shell = Window.Current.Content as AppShell;
            shell.AppFrame.Navigate(typeof(NowPlaying));
        }
        catch (Exception ex)
        {
            MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
            await showMessgae.ShowAsync();
        }
    }

    public async Task<string> GetjsonStream(string url) //Function to read from given url
    {
        HttpClient client = new HttpClient();
        HttpResponseMessage response = await client.GetAsync(url);
        HttpResponseMessage v = new HttpResponseMessage();
        return await response.Content.ReadAsStringAsync();
    }
}
}

- Add a new Blank Page named Me.xaml and add the following code to the xaml file:

<Page
    x:Class="MusicPlayer.Me"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MusicPlayer"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel HorizontalAlignment="Center">
            <Ellipse Height="250" Grid.Row="0" Width="250" x:Name="Albumart" >
                <Ellipse.Fill>
                    <ImageBrush x:Name="profilePhoto" ImageSource="Assets\Albumart.png" Stretch="UniformToFill" />
                </Ellipse.Fill>
            </Ellipse>
            <TextBlock Text="First Name" Style="{StaticResource TitleTextBlockStyle}" Foreground="Gray" />
            <TextBlock x:Name="txtFirstname" Text="" Style="{StaticResource HeaderTextBlockStyle}" />
            <TextBlock Text="Last Name" Style="{StaticResource TitleTextBlockStyle}" Foreground="Gray" />
            <TextBlock x:Name="txtlastname"  Text="" Style="{StaticResource HeaderTextBlockStyle}" />
            <TextBlock Text="City" Style="{StaticResource TitleTextBlockStyle}" Foreground="Gray" />
            <TextBlock x:Name="txtCity"  Text="" Style="{StaticResource HeaderTextBlockStyle}" />
            <TextBlock Text="Country" Style="{StaticResource TitleTextBlockStyle}" Foreground="Gray" />
            <TextBlock x:Name="txtCountry"  Text="" Style="{StaticResource HeaderTextBlockStyle}" />
            <TextBlock Text="Website" Style="{StaticResource TitleTextBlockStyle}" Foreground="Gray" />
            <TextBlock x:Name="txtWebsite"  Text="" Style="{StaticResource HeaderTextBlockStyle}" />
            <TextBlock Text="Following" Style="{StaticResource TitleTextBlockStyle}" Foreground="Gray" />
            <TextBlock x:Name="txtFollowing"  Text="" Style="{StaticResource HeaderTextBlockStyle}" />
            <TextBlock Text="Followers" Style="{StaticResource TitleTextBlockStyle}" Foreground="Gray" />
            <TextBlock x:Name="txtFollowers"  Text="" Style="{StaticResource HeaderTextBlockStyle}" />
        </StackPanel>
    </Grid>
</Page>

In the above code, we added few controls to display the Profile Photo, Name, Website and some other details from SoundCloud.

Add the following code to Me.xaml.cs to populate profile details from the new SoundCloudUser object.

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace MusicPlayer
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class Me : Page
    {
        public Me()
        {
            this.InitializeComponent();
            this.Loaded += Me_Loaded;
        }

        private void Me_Loaded(object sender, RoutedEventArgs e)
        {
            if (App.SCUser != null)
            {
                txtFirstname.Text = Convert.ToString(App.SCUser.first_name);
                txtlastname.Text = Convert.ToString(App.SCUser.last_name);
                txtWebsite.Text = Convert.ToString(App.SCUser.website);
                txtCity.Text = Convert.ToString(App.SCUser.city);
                txtCountry.Text = Convert.ToString(App.SCUser.country);
                txtFollowers.Text = Convert.ToString(App.SCUser.followers_count);
                txtFollowing.Text = Convert.ToString(App.SCUser.followings_count);
                profilePhoto.ImageSource = new BitmapImage(new Uri(App.SCUser.avatar_url));

            }
        }
    }
}

- Now add the following code changes to NowPlaying.xaml.cs.

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace MusicPlayer
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class NowPlaying : Page
{
    private bool isMyBackgroundTaskRunning = false;
    AppShell shell = Window.Current.Content as AppShell;
    private AutoResetEvent backgroundAudioTaskStarted;

    /// <summary>
    /// Gets the information about background task is running or not by reading the setting saved by background task.
    /// This is used to determine when to start the task and also when to avoid sending messages.
    /// </summary>
    private bool IsMyBackgroundTaskRunning
    {
        get
        {
            if (isMyBackgroundTaskRunning)
                return true;

            string value = ApplicationSettingsHelper.ReadResetSettingsValue(ApplicationSettingsConstants.BackgroundTaskState) as string;
            if (value == null)
            {
                return false;
            }
            else
            {
                try
                {
                    isMyBackgroundTaskRunning = EnumHelper.Parse<BackgroundTaskState>(value) == BackgroundTaskState.Running;
                }
                catch (ArgumentException)
                {
                    isMyBackgroundTaskRunning = false;
                }
                return isMyBackgroundTaskRunning;
            }
        }
    }


    public NowPlaying()
    {
        this.InitializeComponent();
        this.Loaded += NowPlaying_Loaded;
    }

    private void NowPlaying_Loaded(object sender, RoutedEventArgs e)
    {
        backgroundAudioTaskStarted = new AutoResetEvent(false);
        if (!IsMyBackgroundTaskRunning)
        {
            StartBackgroundAudioTask();
        }
        else
        {
            //Start playback if Paused.
            if (MediaPlayerState.Paused == BackgroundMediaPlayer.Current.CurrentState)
            {
                BackgroundMediaPlayer.Current.Play();
            }
            else if (MediaPlayerState.Closed == BackgroundMediaPlayer.Current.CurrentState)
            {
                StartBackgroundAudioTask();
            }
        }

    }

    private void StartBackgroundAudioTask()
    {
        AddMediaPlayerEventHandlers();
        var startResult = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            bool result = backgroundAudioTaskStarted.WaitOne(10000);
            //Send message to initiate playback
            if (result == true)
            {
                MessageService.SendMessageToBackground(new UpdatePlaylistMessage(App.likes));
                MessageService.SendMessageToBackground(new StartPlaybackMessage());

            }
            else
            {
                throw new Exception("Background Audio Task didn't start in expected time");
            }
        });
    }

    private void AddMediaPlayerEventHandlers()
    {
        BackgroundMediaPlayer.MessageReceivedFromBackground += this.BackgroundMediaPlayer_MessageReceivedFromBackground;
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        //Update UI based on Track Id stored.
        var trackId = GetCurrentTrackId();
        if (trackId != null)
        {
            var song = App.likes.Where(t => t.stream_url == trackId.ToString()).FirstOrDefault();
            LoadTrack(song);
        }

    }

    async void BackgroundMediaPlayer_MessageReceivedFromBackground(object sender, MediaPlayerDataReceivedEventArgs e)
    {
        TrackChangedMessage trackChangedMessage;
        if (MessageService.TryParseMessage(e.Data, out trackChangedMessage))
        {
            // When foreground app is active change track based on background message
            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                var songIndex = GetSongIndexById(trackChangedMessage.TrackId);
                if (songIndex >= 0)
                {
                    var song = App.likes[songIndex];
                    LoadTrack(song); //Update UI
                }
            });
            return;
        }

        BackgroundAudioTaskStartedMessage backgroundAudioTaskStartedMessage;
        if (MessageService.TryParseMessage(e.Data, out backgroundAudioTaskStartedMessage))
        {
            backgroundAudioTaskStarted.Set();
            return;
        }
    }

    public int GetSongIndexById(Uri id)
    {
        return App.likes.FindIndex(s => new Uri(s.stream_url) == id);
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        if (isMyBackgroundTaskRunning)
        {
            ApplicationSettingsHelper.SaveSettingsValue(ApplicationSettingsConstants.BackgroundTaskState, BackgroundTaskState.Running.ToString());
        }

        base.OnNavigatedFrom(e);
    }

    private Uri GetCurrentTrackId()
    {
        object value = ApplicationSettingsHelper.ReadResetSettingsValue(ApplicationSettingsConstants.TrackId);
        if (value != null)
            return new Uri((String)value);
        else
            return null;
    }

    private async void LoadTrack(SoundCloudTrack currentTrack)
    {
        try
        {
            //Change album art
            string albumartImage = Convert.ToString(currentTrack.artwork_url);
            if (string.IsNullOrWhiteSpace(albumartImage))
            {
                albumartImage = @"ms-appx:///Assets/Albumart.png";

            }
            else
            {
                albumartImage = albumartImage.Replace("-large", "-t500x500");
            }

            albumrtImage.ImageSource = new BitmapImage(new Uri(albumartImage));

            //Change Title and User name
            txtSongTitle.Text = currentTrack.title;
            txtAlbumTitle.Text = Convert.ToString(currentTrack.user.username);

        }
        catch (Exception ex)
        {
            MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
            await showMessgae.ShowAsync();
        }
    }

    private void btnPlay_Click(object sender, RoutedEventArgs e)
    {
        if (IsMyBackgroundTaskRunning)
        {
            if (MediaPlayerState.Playing == BackgroundMediaPlayer.Current.CurrentState)
            {
                BackgroundMediaPlayer.Current.Pause();
            }
            else if (MediaPlayerState.Paused == BackgroundMediaPlayer.Current.CurrentState)
            {
                BackgroundMediaPlayer.Current.Play();
            }
            else if (MediaPlayerState.Closed == BackgroundMediaPlayer.Current.CurrentState)
            {
                StartBackgroundAudioTask();
            }
        }
        else
        {
            StartBackgroundAudioTask();
        }
    }

    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
        //Send message to background task
        MessageService.SendMessageToBackground(new SkipNextMessage());

    }

    private void btnPrev_Click(object sender, RoutedEventArgs e)
    {
        //Send message to background task
        MessageService.SendMessageToBackground(new SkipPreviousMessage());
    }

}
}

In the above code, we have done the following:

1. As soon as the Page is loaded, we check if the BackgroundTask is currently running and toggle playback.

2. If this is the first time or the Background task is not running, we start a new BackgroundTask and enable App to Background Task messaging.

3. This allows us to control the Media Player from our UI as well as get notified if Media controls (Keyboard, Phone Hardware buttons etc) are pressed, which can be used to display correct Track details if our App is in the foreground.

4. We also send respective Next/Previous Message to Background Task to move between songs in Playlist.

BackgroundAudioTask

-Make the following code changes to MyBackgroundAudioTask.cs file.

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace MusicPlayer
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class NowPlaying : Page
{
    private bool isMyBackgroundTaskRunning = false;
    AppShell shell = Window.Current.Content as AppShell;
    private AutoResetEvent backgroundAudioTaskStarted;

    /// <summary>
    /// Gets the information about background task is running or not by reading the setting saved by background task.
    /// This is used to determine when to start the task and also when to avoid sending messages.
    /// </summary>
    private bool IsMyBackgroundTaskRunning
    {
        get
        {
            if (isMyBackgroundTaskRunning)
                return true;

            string value = ApplicationSettingsHelper.ReadResetSettingsValue(ApplicationSettingsConstants.BackgroundTaskState) as string;
            if (value == null)
            {
                return false;
            }
            else
            {
                try
                {
                    isMyBackgroundTaskRunning = EnumHelper.Parse<BackgroundTaskState>(value) == BackgroundTaskState.Running;
                }
                catch (ArgumentException)
                {
                    isMyBackgroundTaskRunning = false;
                }
                return isMyBackgroundTaskRunning;
            }
        }
    }


    public NowPlaying()
    {
        this.InitializeComponent();

        this.Loaded += NowPlaying_Loaded;
    }

    private void NowPlaying_Loaded(object sender, RoutedEventArgs e)
    {
        backgroundAudioTaskStarted = new AutoResetEvent(false);

        if (!IsMyBackgroundTaskRunning)
        {
            StartBackgroundAudioTask();
        }
        else
        {
            //Start playback if Paused.
            if (MediaPlayerState.Paused == BackgroundMediaPlayer.Current.CurrentState)
            {
                BackgroundMediaPlayer.Current.Play();
            }
            else if (MediaPlayerState.Closed == BackgroundMediaPlayer.Current.CurrentState)
            {
                StartBackgroundAudioTask();
            }
        }

    }

    private void StartBackgroundAudioTask()
    {
        AddMediaPlayerEventHandlers();
        var startResult = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            bool result = backgroundAudioTaskStarted.WaitOne(10000);
            //Send message to initiate playback
            if (result == true)
            {
                MessageService.SendMessageToBackground(new UpdatePlaylistMessage(App.likes));
                MessageService.SendMessageToBackground(new StartPlaybackMessage());

            }
            else
            {
                throw new Exception("Background Audio Task didn't start in expected time");
            }
        });
    }

    private void AddMediaPlayerEventHandlers()
    {
        BackgroundMediaPlayer.MessageReceivedFromBackground += this.BackgroundMediaPlayer_MessageReceivedFromBackground;
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        //Update UI based on Track Id stored.
        var trackId = GetCurrentTrackId();
        if (trackId != null)
        {
            var song = App.likes.Where(t => t.stream_url == trackId.ToString()).FirstOrDefault();
            LoadTrack(song);
        }

    }



    async void BackgroundMediaPlayer_MessageReceivedFromBackground(object sender, MediaPlayerDataReceivedEventArgs e)
    {
        TrackChangedMessage trackChangedMessage;
        if (MessageService.TryParseMessage(e.Data, out trackChangedMessage))
        {
            // When foreground app is active change track based on background message
            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                var songIndex = GetSongIndexById(trackChangedMessage.TrackId);
                if (songIndex >= 0)
                {
                    var song = App.likes[songIndex];
                    LoadTrack(song); //Update UI
                }
            });
            return;
        }

        BackgroundAudioTaskStartedMessage backgroundAudioTaskStartedMessage;
        if (MessageService.TryParseMessage(e.Data, out backgroundAudioTaskStartedMessage))
        {
            backgroundAudioTaskStarted.Set();
            return;
        }
    }

    public int GetSongIndexById(Uri id)
    {
        return App.likes.FindIndex(s => new Uri(s.stream_url) == id);
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        if (isMyBackgroundTaskRunning)
        {
            ApplicationSettingsHelper.SaveSettingsValue(ApplicationSettingsConstants.BackgroundTaskState, BackgroundTaskState.Running.ToString());
        }

        base.OnNavigatedFrom(e);
    }

    private Uri GetCurrentTrackId()
    {
        object value = ApplicationSettingsHelper.ReadResetSettingsValue(ApplicationSettingsConstants.TrackId);
        if (value != null)
            return new Uri((String)value);
        else
            return null;
    }


    private async void LoadTrack(SoundCloudTrack currentTrack)
    {
        try
        {
            //Change album art
            string albumartImage = Convert.ToString(currentTrack.artwork_url);
            if (string.IsNullOrWhiteSpace(albumartImage))
            {
                albumartImage = @"ms-appx:///Assets/Albumart.png";

            }
            else
            {
                albumartImage = albumartImage.Replace("-large", "-t500x500");
            }

            albumrtImage.ImageSource = new BitmapImage(new Uri(albumartImage));

            //Change Title and User name
            txtSongTitle.Text = currentTrack.title;
            txtAlbumTitle.Text = Convert.ToString(currentTrack.user.username);

        }
        catch (Exception ex)
        {
            MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
            await showMessgae.ShowAsync();
        }
    }

    private void btnPlay_Click(object sender, RoutedEventArgs e)
    {
        if (IsMyBackgroundTaskRunning)
        {
            if (MediaPlayerState.Playing == BackgroundMediaPlayer.Current.CurrentState)
            {
                BackgroundMediaPlayer.Current.Pause();
            }
            else if (MediaPlayerState.Paused == BackgroundMediaPlayer.Current.CurrentState)
            {
                BackgroundMediaPlayer.Current.Play();
            }
            else if (MediaPlayerState.Closed == BackgroundMediaPlayer.Current.CurrentState)
            {
                StartBackgroundAudioTask();
            }
        }
        else
        {
            StartBackgroundAudioTask();
        }
    }

    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
        //Send message to background task
        MessageService.SendMessageToBackground(new SkipNextMessage());

    }

    private void btnPrev_Click(object sender, RoutedEventArgs e)
    {
        //Send message to background task
        MessageService.SendMessageToBackground(new SkipPreviousMessage());
    }

}
}

Here we updated the song model with SoundCloudTack custom object, handled messages from Foreground App, saved current Track Id and changed tracks.

BackgroundAudioShared

- Update SoundCloudUser.cs file with the following code to add new properties which are used in Me.xaml file in MusicPlayer project.

namespace BackgroundAudioShared
{
    public class SoundCloudUser
    {
        public int id { get; set; }
        public string permalink { get; set; }
        public string username { get; set; }
        public string uri { get; set; }
        public string permalink_url { get; set; }
        public string avatar_url { get; set; }

        public string kind { get; set; }
        public string last_modified { get; set; }
        public object country { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
        public string full_name { get; set; }
        public object description { get; set; }
        public object city { get; set; }
        public object discogs_name { get; set; }
        public object myspace_name { get; set; }
        public object website { get; set; }
        public object website_title { get; set; }
        public bool online { get; set; }
        public int track_count { get; set; }
        public int playlist_count { get; set; }
        public string plan { get; set; }
        public int public_favorites_count { get; set; }
        public int followers_count { get; set; }
        public int followings_count { get; set; }
    }
}

- Change UpdatePlaylistMessage.cs to use SoundCloudTrack object.

namespace BackgroundAudioShared.Messages
{
    [DataContract]
    public class UpdatePlaylistMessage
    {
        public UpdatePlaylistMessage(List<SoundCloudTrack> songs)
        {
            this.Songs = songs;
        }
           
        [DataMember]
        public List<SoundCloudTrack> Songs;
    }
}

 

Testing our Windows 10 Sound Cloud Music Player app

Press debug and test the changes and if there are no compile errors, you should see the changes in Profile, Likes and Now Playing Pages as shown here.

likes

userprofile

music-player

Clicking any Track in Likes page will change the “now playing” Track to the selected track. Press any of the hardware buttons to show Media Controls.

media-controls

Minimize the Music Player app and enjoy the music J

Windows 10 Application Deployment tool

Windows 10 Application Deployment tool (WinAppDeployCmd.exe) is a command line utility that can be used to remotely deploy Apps to Windows Mobile Devices.

Let us try to deploy Music Player App to a Windows Mobile.

- Open Command Prompt and navigate to C:\Program Files (x86)\Windows Kits\10\bin\x86

- Type WinAppDeployCmd devices and press enter to display the list of available remote devices.

remote-devices

Make sure you enable developer features in your device and pair it with your development PC.

dev-mode

- Create App Package for MusicPlayer by Right click MusicPlayer project -> Store -> Create App Packages…

- In the popup message. Select No for “Do you want to build packages to upload to the Windows Store” and click Next.

- Choose a folder and click create to create App Packages (Make sure ARM Architecture is selected)

- Type WinAppDeployCmd install -file "MusicPlayer_2.0.1.0_arm_Debug.appxbundle" ip [YOUR REMOTE IP HERE] and press enter to install the App Package.

- Wait for the App to be installed in your Windows Mobile device

win-app-deploy-cmd

Open the App in Windows Mobile device and see how our App interacts with Media Controls and plays Audio in the background

windows-mobile-screenshot

Raspberry Pi 2

Let us deploy our Music Player app to a Raspberry Pi 2 which is running Windows 10 IoT Core and see how the App works in a complete new device family without any change.

Setup your PC by following the Getting Started Instructions here http://bit.ly/1DQVtr3 . Then download the latest preview version of Windows 10 IoT Core from downloads Page http://bit.ly/1GBq9XR

Setup your Raspberry Pi 2 by following the instructions here http://bit.ly/1dLQTOp

If everything is setup properly, you will see your Raspberry Pi 2 listed in Windows IoT Core Watcher.

iot-corewatcher

Open the Music Player solution in Visual Studio and follow these steps to deploy the App.

- Right click Music Player project and select Properties

- Click Debug tab and Select Target Device to Remote Machine

- Enter the ip address of your Raspberry Pi2 or click Find to scan for multiple devices and select the Target device.

projproperties

- Change Target platform to ARM

targetplatform

- Press F5 to deploy and debug the App or simply Right click Music Player Project -> Deploy to deploy the App and start enjoying your Sound Cloud Music player in Raspberry Pi 2.

raspberry-win10

raspberry-pi2

Windows 10 IoT core can run only one App in the foreground. To increase the performance, the raspberry Pi version of the Music Player app can be simplified by removing the background Task and using MediaElement or playback as shown in the first part of this series.

Download the entire source code from GitHub at
bit.ly/dncm21-win10uwp

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
ShobanKumar is an ex-Microsoft MVP in SharePoint who currently works as a SharePoint Consultant. You can read more about his projects at http://shobankumar.com. You can also follow him on twitter @shobankr


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!

Categories

JOIN OUR COMMUNITY

POPULAR ARTICLES

FREE .NET MAGAZINES

Free DNC .NET Magazine

Tags

JQUERY COOKBOOK

jQuery CookBook