Windows 10 Anniversary Update (1607) and UWP Apps – Connected Apps and Devices via Rome

I tried something out for the first time today that really caught my interest and so I thought I’d share.

I first heard reference to ‘Project Rome’ back at //build/ 2016 but I must admit that I hadn’t found time to watch the session about it properly until recently and that’s partly just due to ‘never having time’ and also partly because I had started to watch it and it didn’t seem to immediately leap into the topic and so I’d put it to one side.

I should have had more faith Smile because it’s a really good session and it’s absolutely worth watching – click the image below to jump into it (it’s short at around 30mins).

image

There’s two discrete parts to that session.

The first part about ‘Web to App Linking’. The core idea there is that an app can register itself (under certain circumstances and user control) to be a handler for what I’d call a ‘portion of the HTTP/HTTPS namespace’ such that if the device comes across a URI like “http://www.myAmazingApp.com” then it’s possible that when the system launches that URI the result ends up in the app from ‘My Amazing App’ rather than the standard system action which would be to launch the browser.

That’s written up really well here;

Support web-to-app linking with app URI handlers

But that //build/ 2016 session then switches at the 15m mark to talk about ‘Project Rome’ which feels like it is doing some of the legwork on the idea of an experience which spans devices rather than being confined to a single device.

There’s been lots of talk at Microsoft around ‘the mobility of the experience’ and it feels to me that ‘Project Rome’ has a part to play in that idea by providing some pieces of a framework which allows for asking questions about a user’s set of devices and then taking higher level actions across that set of devices.

I wanted to try it out for the first time so having watched the video above I went and got my developer laptop running 1607 and I started to see if the docs for classes like RemoteSystem that I spotted in the video were online (they are).

I then paused for a while as I hadn’t really considered whether I had multiple devices running Windows 10 1607 to test on until I remembered that I had a home all-in-one that I’d upgraded to 1607 which is sitting in another room on standby with some active logon sessions.

It’s worth saying that the set of devices that is being worked with here is the set of devices that you see when you go to;

http://account.microsoft.com

and then have a look at your list of Devices there;

Untitled

and I have this home office PC which shows up there.

Capture

As is often the case, I’d started to write a little bit of code when I found that there’s an article which brings some of these classes together;

Launch an app on a remote device

and that led me to the higher level documentation;

Connected apps and devices (Project “Rome”)

and my ‘Hello World’ is pretty much a duplicate of what happens in that former article in that I made simple XAML page which presents a data-bound ListView and a Button;

  <Grid
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition
        Height="Auto" />
    </Grid.RowDefinitions>
    <ListView
      Margin="8"
      Header="Remote Systems"
      ItemsSource="{x:Bind RemoteSystems}"
      SelectionMode="Single"
      SelectedItem="{x:Bind SelectedSystem,Mode=TwoWay}">
      <ListView.ItemContainerStyle>
        <Style
          TargetType="ListViewItem">
          <Setter
            Property="HorizontalContentAlignment"
            Value="Stretch" />
        </Style>
      </ListView.ItemContainerStyle>
      <ListView.ItemTemplate>
        <DataTemplate
          x:DataType="remote:RemoteSystem">
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition />
              <ColumnDefinition />
              <ColumnDefinition />
              <ColumnDefinition />
              <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock
              Text="{x:Bind DisplayName}" />
            <TextBlock
              Text="{x:Bind Id}"
              Grid.Column="1" />
            <TextBlock
              Text="{x:Bind Kind}"
              Grid.Column="2" />
            <TextBlock
              Text="{x:Bind Status}"
              Grid.Column="3" />
            <TextBlock
              Text="{x:Bind IsAvailableByProximity}"
              Grid.Column="4" />
          </Grid>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
    <Button
      Margin="8"
      Grid.Row="1"
      HorizontalContentAlignment="Center"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Stretch"
      Click="{x:Bind OnLaunchUri}">
      <Button.Content>
        <StackPanel Orientation="Horizontal"
                    HorizontalAlignment="Center">
          <SymbolIcon
            Symbol="Map"/>
          <TextBlock
            Margin="4,0,0,0"
            Text="launch map of New York" />
        </StackPanel>
      </Button.Content>
    </Button>
  </Grid>

and then added some code-behind to see if I could get the basics of this to work;

namespace App24
{
  using System;
  using System.Collections.ObjectModel;
  using System.ComponentModel;
  using System.Linq;
  using System.Runtime.CompilerServices;
  using System.Threading.Tasks;
  using Windows.System;
  using Windows.System.RemoteSystems;
  using Windows.UI.Core;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;

  public sealed partial class MainPage : Page, INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    public MainPage()
    {
      this.InitializeComponent();
      this.Loaded += OnLoaded;
      this.RemoteSystems = new ObservableCollection<RemoteSystem>();
    }
    public ObservableCollection<RemoteSystem> RemoteSystems
    {
      get; private set;
    }
    public RemoteSystem SelectedSystem
    {
      get
      {
        return (this.selectedSystem);
      }
      set
      {
        if (this.selectedSystem != value)
        {
          this.selectedSystem = value;
          this.FirePropertyChanged();
        }
      }
    }
    // Note, this is bound to from an x:Bind, just trying that out for the
    // first time.
    public async void OnLaunchUri()
    {
      if (this.SelectedSystem != null)
      {
        var request = new RemoteSystemConnectionRequest(this.SelectedSystem);

        await RemoteLauncher.LaunchUriAsync(
          request, 
          new Uri("bingmaps:?cp=40.726966~-74.006076&lvl=10&where=New%20York "));
      }
    }
    void FirePropertyChanged([CallerMemberName] string prop = "")
    {
      this.PropertyChanged?.Invoke(
        this, new PropertyChangedEventArgs(prop));
    }
    async void OnLoaded(object sender, RoutedEventArgs e)
    {
      var status = await RemoteSystem.RequestAccessAsync();

      if (status == RemoteSystemAccessStatus.Allowed)
      {
        this.watcher = RemoteSystem.CreateWatcher();
        this.watcher.RemoteSystemAdded += OnAdded;
        this.watcher.RemoteSystemRemoved += OnRemoved;
        this.watcher.RemoteSystemUpdated += OnUpdated;
        this.watcher.Start();
      }
    }
    async void OnUpdated(RemoteSystemWatcher sender, RemoteSystemUpdatedEventArgs args)
    {
      await this.DispatchAsync(
        () =>
        {
          // Laziness on my part not wanting to create a view model for a remote system
          // with property change notification etc.
          this.Remove(args.RemoteSystem.Id);
          this.Add(args.RemoteSystem);
        }
      );
    }
    async void OnRemoved(RemoteSystemWatcher sender, RemoteSystemRemovedEventArgs args)
    {
      await this.DispatchAsync(
        () =>
        {
          this.Remove(args.RemoteSystemId);
        }
      );
    }
    async void OnAdded(RemoteSystemWatcher sender, RemoteSystemAddedEventArgs args)
    {
      await this.DispatchAsync(
        () =>
        {
          this.Add(args.RemoteSystem);

        }
      );
    }
    void Add(RemoteSystem remoteSystem)
    {
      this.RemoteSystems.Add(remoteSystem);
    }
    async Task DispatchAsync(DispatchedHandler action)
    {
      await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, action);
    }
    void Remove(string remoteSystemId)
    {
      var entry = this.RemoteSystems.SingleOrDefault(system => system.Id == remoteSystemId);

      if (entry != null)
      {
        this.RemoteSystems.Remove(entry);
      }
    }
    RemoteSystem selectedSystem;
    RemoteSystemWatcher watcher;
  }
}

and so most of this code is just using the RemoteSystemWatcher class and, having asked for permission, this is going to deliver RemoteSystem information to me via events for Added/Removed/Updated. I use that to build up an ObservableCollection which is bound into the ListView on the screen and I also data-bind the SelectedSystem to the selected item in that ListView.

I liked that the RemoteSystemWatcher follows what feels like a pattern that’s consistent with (e.g.) looking for AllJoyn devices or looking for Bluetooth LE devices – there are quite a few ‘watcher’ objects within the UWP APIs and this one feels natural to use.

I hooked the button press to then construct a RemoteSystemConnectionRequest instance and to do a LaunchUriAsync on that remote system and I’m letting the default options stand here but it’s possible to specify the list of apps that I’d prefer handle the request remotely and also any launch parameters to be passed to the remote app.

I must admit that the first time that I ran this code, I was not expecting success. It seemed complex enough to be unlikely to work and I figured that there’d likely be some configuring and messing around to do.

I got success Smile 

The little UI listed the all-in-one office machine that had been sitting in another room on standby the whole time I’d been writing this code;

Capture

and I tapped on the button, went off to visit that machine and (sure enough) there was the Maps app displaying a map of New York.

I like this a lot Winking smile

Being an awkward type, I logged myself out of that machine to see if the status would go ‘Unavailable’ and it didn’t seem to and so I tried to launch the Maps app remotely again without an active logon session on that machine and this caused me to add a bit of code to display the return value from the LaunchUriAsync method;

        var response = await RemoteLauncher.LaunchUriAsync(
          request, 
          new Uri("bingmaps:?cp=40.726966~-74.006076&lvl=10&where=New%20York "));

        var messageDialog = new MessageDialog(response.ToString(), "return status");

        await messageDialog.ShowAsync();

and, sure enough, this now returned a RemoteSystemUnavailable status even though the RemoteSystem itself was still claiming availability so I’m not sure quite how that ‘availability’ status works and when it updates.

I also noticed a very interesting thing in that when I logged back in to that system the Maps app popped up with a map of New York – it seems like my request has been queued somewhere and delivered at a later point in time but I’d need to dig into that more to understand whether that’s by design or not.

Being an awkward type, I took the machine that is acting as a client here off the network that it shares with the ‘mthome’ PC and moved it to a 4G network to see what might happen there and it worked just the same way as you might expect.

I wondered how I might get these 2 PCs to be ‘proximally’ connected without a cloud connection and so I played with a few options there but have yet to figure out quite how that works for 2 PCs, maybe I’d have more luck with a phone for that scenario but it needs returning to.

Being able to launch an app on a remote machine with parameters opens up a tonne of scenarios which involve transferring a task from one device to another and especially when you think that those parameters might be some identifier to a piece of shared state in the cloud but there’s another part of this ‘Rome’ framework that interests me here too and that’s the idea of;

Communicate with a remote app service

and that gives us the possibility of an app on one device making use of app services from an app on another device.

That’s a topic for another post but it seems really quite powerful in that it has the potential to open up an opportunity to run the constituent pieces of an application on the machines that I think are best suited to the task at the time. I could see examples where I might choose to run some processing on a remote device because it has more processing power or a better (or cheaper, or more reliable) connection to the internet.

I want to dig into that but, again, that’s for another post…

6 thoughts on “Windows 10 Anniversary Update (1607) and UWP Apps – Connected Apps and Devices via Rome

  1. Pingback: Windows 10 Anniversary Update (1607) and UWP Apps – Connected Apps and Devices via Rome - How to Code .NET

  2. Very interesting read, I’d heard about this in brief previously but hadn’t seen (or spent time to find) an example so far. I’m definitely going to look into this further.

    Especially interested to see if I can launch apps on and from a Phone in the same way, I’m hoping that’s possible and think it will be with this being Windows 10.

    • Hi,

      Yes, I think the intention here is that this works for whatever devices you have running 1607 that are associated by their use of the same Microsoft Account (or Live Id).

      Mike

  3. This is somewhat unrelated, but I was wondering when the v2.2 Kinect Runtime docs will be made available. There are some new bits there that appear to target UWP. The runtime is being pushed out now.

Comments are closed.