Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Using a Surface Dial to Scroll Through a Playing Track

DZone's Guide to

Using a Surface Dial to Scroll Through a Playing Track

If you've been thinking of picking up the Surface Dial, check out how you can program it to do your bidding! At least when it comes to music.

· IoT Zone ·
Free Resource
So, I'm very impressed with the new Surface Dial. It's smaller than I was expecting but feels really nice to use. Plus it's easy to program!

Official links are here. Or see the sample I put together below:
Yes, it's an ugly app but I wanted to see how easy it would be to use it to skip back and forwards through a playing track. I'll often be listening to podcasts and skip back a little bit to have something repeated. I thought this would be a suitable tool for such a task.

It's a simple app.
  • Pick a track and it starts playing.
  • Tap the dial to toggle pause and playback.
  • Scroll the dial to move forwards and backward through the track.
  • There's also a button to toggle playback and a visualization of how far through the track it is.
Point of note:
  • It's possible to automatically select the (radial) menu item but I couldn't get it to set in the page constructor, OnNavigatedTo event, or the Loaded event. Because it's tied to the window (so different apps can have different menu items selected) I can understand why it needs a window available but I would have thought this possible in the Loaded event. Instead, I set this when a track is picked in my code.

There's lots more that can be done with the dial. Including:
  • Handling velocity of scrolling
  • Haptic feedback
  • Placement on screen (but I'll need a Surface Studio for that)
Code embedded below and here
<Page
    x:Class="MediaDial.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    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 Padding="50">
            <MediaElement x:Name="MediaElement" CurrentStateChanged="MediaElement_OnCurrentStateChanged" />
            <Button Click="PickTrackClicked">pick track</Button>
            <Button Click="PlayPauseClicked" Margin="0,10">Play/Pause</Button>
            <ProgressBar x:Name="Progress" Maximum="100" Minimum="0" Value="0" Height="20" />
        </StackPanel>
    </Grid>
</Page>


using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage.Pickers;
using Windows.UI.Core;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace MediaDial
{
    public sealed partial class MainPage : Page
    {
        private RadialController Controller;
        private RadialControllerMenuItem menuItem;

        public MainPage()
        {
            this.InitializeComponent();

            Controller = RadialController.CreateForCurrentView();
            Controller.RotationResolutionInDegrees = 1;

            Controller.RotationChanged += Controller_RotationChanged;
            Controller.ButtonClicked += Controller_ButtonClicked;

            this.menuItem = RadialControllerMenuItem.CreateFromKnownIcon("MediaDial", RadialControllerMenuKnownIcon.NextPreviousTrack);

            Controller.Menu.Items.Add(this.menuItem);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            Controller?.Menu.Items.Clear();
        }

        private void Controller_ButtonClicked(RadialController sender, RadialControllerButtonClickedEventArgs args)
        {
            TogglePlayback();
        }

        private void Controller_RotationChanged(RadialController sender, RadialControllerRotationChangedEventArgs args)
        {
            var delta = args.RotationDeltaInDegrees;

            if (delta > 0)
            {
                MediaElement.Position += TimeSpan.FromSeconds(1);
            }
            else
            {
                MediaElement.Position -= TimeSpan.FromSeconds(1);
            }

            if (MediaElement.CurrentState != MediaElementState.Playing)
            {
                SetPosition();
            }
        }

        private void PlayPauseClicked(object sender, RoutedEventArgs e)
        {
            TogglePlayback();
        }

        private void TogglePlayback()
        {
            if (MediaElement.CurrentState == MediaElementState.Playing)
            {
                this.MediaElement.Pause();
            }
            else
            {
                this.MediaElement.Play();
            }
        }

        private async void PickTrackClicked(object sender, RoutedEventArgs e)
        {
            var picker = new FileOpenPicker();
            picker.SuggestedStartLocation = PickerLocationId.MusicLibrary;
            picker.FileTypeFilter.Add(".wmv");
            picker.FileTypeFilter.Add(".mp3");

            var file = await picker.PickSingleFileAsync();
            if (file != null)
            {
                var mediaSource = MediaSource.CreateFromStorageFile(file);
                var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

                MediaElement.SetPlaybackSource(mediaPlaybackItem);
            }

            Controller?.Menu.SelectMenuItem(this.menuItem);
        }

        private async void MediaElement_OnCurrentStateChanged(object sender, RoutedEventArgs e)
        {
            if (MediaElement.CurrentState == MediaElementState.Playing)
            {
                await Dispatcher.RunAsync(
                        CoreDispatcherPriority.Normal,
                        async () => await RunTimer());
            }
            else
            {
                SetPosition();
            }
        }

        private async Task RunTimer()
        {
            while (MediaElement.CurrentState == MediaElementState.Playing)
            {
                SetPosition();

                await Task.Delay(1000);
            }
        }

        private void SetPosition()
        {
            try
            {
                Progress.Value = 100 / MediaElement.NaturalDuration.TimeSpan.TotalSeconds *
                         MediaElement.Position.TotalSeconds;
            }
            catch (Exception exc)
            {
                Debug.WriteLine(exc);
            }
        }
    }
}


And if you're interested, here are some other SurfaceDial projects to check out.

Topics:
microsoft surface ,dial ,iot

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}