Project Rome 從 //Build 2016 發表的技術,讓 App 可以在同一個 Microsoft account (MSA) 的不同設備(Windows, Android, iOS)互相溝通。
這一篇將介紹如何操作 Remote Systems APIs 做到這些應用找尋設備,啓動遠端設備中的 App 與 App Service。
重點提醒
- 在 Windows 10, Version 1607 開始 Remote Systems APIs 支援讓 App 從一個設備開始處理任務,最後到另一臺設備的完成它。
- 設備能經由 bluetooth, wireless 或是 cloud (則需要相同的 Microsoft account (MSA) 連結)
- 常見的情境:
- 用戶可能在車上用手機聼歌曲,等他回到家裏就可以直接把目前播放的進度改交給 Xbox One 繼續播放
- 利用 App Service 建立 channel 讓兩個設備直接互相溝通或控制
- 開發的專案要宣告 RemoteSystem 的 capability:
<Capabilities> <Capability Name="internetClient" /> <uap3:Capability Name="remoteSystem" /> </Capabilities>
- 如果要允許不同帳號也可以控制你的設備 (使用 RemoteSystemAuthorizationKind.Anonymous),需要開 跨裝置體驗 的設定,如下圖:
有了觀念之後,下面介紹細部的開發。
* 如何找到遠端設備
1. 利用 RemoteSystemWatcher 找出設備:
RemoteSystemWatcher 搭配 Filter 找出符合的設備,Filter 可以指定:
- RemoteSystemDiscoveryType:proximal, local network, cloud connection
- RemoteSystemKinds:desktop, mobile, Xbox, Hub, Holographic 等
- RemoteSystemStatus:設備目前的狀態,例如:Available, DiscoveringAvailability, Unavailable, Unknown
- RemoteSystemPlatform:設備的作業系統類型,例如:Android, iOS, Windows, Linux 等
- RemoteSystemAuthorizationKind:設定找尋到設備是否為相同的帳號或是任何設備;如果是不同用戶,必須是狀態為 available 且經由 proximal connection 方式的才能使用;如果沒有特別設定 RemoteSystemAuthorizationKindFilter 的話,預設值為 same-user 的設備才能被找到。
private async void OnStartDiscoverClick(object sender, RoutedEventArgs e)
{
// 要使用 RemoteSystem 前,需要先要求用戶給與權限
var requestPermission = await RemoteSystem.RequestAccessAsync();
if (requestPermission != RemoteSystemAccessStatus.Allowed)
{
return;
}
OnStopDiscoverClick(sender, e);
DiscoverDevicesAsync();
}
private RemoteSystemWatcher devicesWatcher;
private ObservableCollection devicesList;
private List GetRemoteSystemFilter()
{
List filters = new List();
// 設定要用什麽方式找設備, 利用 Any 比較多設備可以被找到
filters.Add(new RemoteSystemDiscoveryTypeFilter(RemoteSystemDiscoveryType.Any));
// 設定找到的設備要是什麽狀態
filters.Add(new RemoteSystemStatusTypeFilter(RemoteSystemStatusType.Available));
// 設定要找尋的設備類型
filters.Add(new RemoteSystemKindFilter(new List
{
RemoteSystemKinds.Desktop, RemoteSystemKinds.Laptop, RemoteSystemKinds.Tablet,
RemoteSystemKinds.Phone,
RemoteSystemKinds.Xbox
}));
// 設定是否需要驗證的設備, 如果要相同帳號可以選 SameUser,預設是 SameUser
filters.Add(new RemoteSystemAuthorizationKindFilter(RemoteSystemAuthorizationKind.Anonymous));
return filters;
}
private void DiscoverDevicesAsync()
{
var filters = GetRemoteSystemFilter();
// Filters 需要在建立 RemoteSystemWatcher 建構子一起傳入
devicesWatcher = RemoteSystem.CreateWatcher(filters);
devicesWatcher.RemoteSystemAdded += DevicesWatcher_RemoteSystemAdded;
devicesWatcher.RemoteSystemRemoved += DevicesWatcher_RemoteSystemRemoved;
devicesWatcher.RemoteSystemUpdated += DevicesWatcher_RemoteSystemUpdated;
devicesWatcher.ErrorOccurred += DevicesWatcher_ErrorOccurred;
devicesWatcher.Start();
}
[注意]
- RemoteSystemDiscoveryType 設定 proximal 不保證物理距離接近程度
- 如果需要可靠物理距離接近程度,可以使用 RemoteSystemDiscoveryType.SpatiallyProximal,但只有支援用藍牙找到設備
- 可利用 RemoteSystem 的 RemoteSystem.IsAvailableBySpatialProximity 確認被找到的設備是否在物理接近範圍內
- RemoteSystemDiscoveryType 設定 local network 時,使用的網路 Profile 是 private 與 domin,設備不會在 public 被找到
- 如果設定 RemoteSystemAuthorizationKind.Anonymous 為過濾調整,只能在 proximal 範圍找到設備
2. 利用 IP Address 找到設備
private async Task GetRemoteSystemByIp(string ipAddress)
{
HostName host = new HostName(ipAddress);
// 利用 IP Address 去找設備,如果找不到設備會拿到 null。
return await RemoteSystem.FindByHostNameAsync(host);
}
* 使用 RemoteSystem 連線設備,並啓動特定的 URI
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
{
var remoteSystem = e.ClickedItem as RemoteSystem;
// 檢查設備是否支援需要的特性
// KnownRemoteSystemCapabilities.AppService 與 KnownRemoteSystemCapabilities.LaunchUri
var appService = await remoteSystem.GetCapabilitySupportedAsync(KnownRemoteSystemCapabilities.AppService);
var launchUri = await remoteSystem.GetCapabilitySupportedAsync(KnownRemoteSystemCapabilities.LaunchUri);
if (launchUri)
{
var launchRequest = new RemoteSystemConnectionRequest(remoteSystem);
var result = await RemoteLauncher.LaunchUriAsync(launchRequest, new Uri("https://poumason.blogspot.com"));
Debug.WriteLine(result.ToString());
}
}
利用 KnownRemoteSystemCapabilities 定義了支援那些 Capabilities 的名稱,幫助開發人員確認 RemoteSystem 能支援什麽。
再建立 RemoteSystemConnectionRequest 物件交給 RemoteLauncher 要求 RemoteSystem 執行任務。
* 讓 App Service 支援從另一個設備來呼叫:
任何 Windows-based 的設備都可以當作 client 或是 host,表示 App Service 可以被安裝在任何一個角色來與對方互動。
由於 App Service 的 UI-less 的特性,在呼叫遠端設備中的 App Service 時就無需將應用程式帶到 foreground。
一樣使用 host 與 client 兩個角色來説明:
- host App 需要修改 Package.manifest 支援 SupportsRemoteSystems:
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" IgnorableNamespaces="uap mp uap"> <Applications> <Application> <Extensions> <uap3:Extension Category="windows.appService" EntryPoint="MyAppService.ServiceTask"> <uap3:AppService Name="com.pou.MyApService" SupportsRemoteSystems="true" /> </uap3:Extension> </Extensions> </Application> </Applications> </Package>
- host App 的 App Service 加入處理來自 client 的請求,以 UWP - 介紹 App Service 與新功能 的範例爲主
- client App 利用 Remote Systems 呼叫遠端設備的 App Service:
AppServiceConnection connection = new AppServiceConnection(); connection.AppServiceName = "com.pou.MyApService"; connection.PackageFamilyName = "f9842749-e4c8-4c15-bac8-bc018db1b2ea_s1mb6h805jdtj"; RemoteSystemConnectionRequest appServiceRequest = new RemoteSystemConnectionRequest(remoteSystem); AppServiceConnectionStatus status = await connection.OpenRemoteAsync(appServiceRequest); if (status == AppServiceConnectionStatus.Success) { var message = new ValueSet(); message.Add("cmd", "Query"); message.Add("id", "1234"); AppServiceResponse response = await connection.SendMessageAsync(message); if (response.Status == AppServiceResponseStatus.Success) { if (response.Message["status"] as string == "OK") { Debug.WriteLine(response.Message["name"] as string); } } }
幾個重要的元素:
- RemoteSystems
該類別管理被找到的遠端設備屬性與它可支援的特性。重要的内容:
Type Name Description Properties IsAvailableByProximity 利用 proximal connection (近距離連線,例如:Bluetooth, local network connection)檢查給定的設備是否可以用,而不是透過雲端連線。 IsAvailableBySpatialProximity 通過空間近距離的連接檢查給定的遠端系統是否可用。 Methods CreateWatcher(IIterable ) 搭配設定 RemoteSystemFilter 的條件, 建立 RemoteSystemWatcher 來搜尋 Remote Systems。 FindByHostNameAsync(HostName) 企圖找到特定 IP Address 或 Host Name 的設備 GetCapabilitySupportedAsync(String) 檢查該 RemoteSystem 是否有支援特定功能, 搭配 KnownRemoteSystemCapabilities 使用。 RequestAccessAsync() 每次在使用 RemoteSystem 之前一定要呼叫, 讓用戶允許 App 有權限使用相關特性。 - RemoteSystemConnectionRequest
代表與特定設備連線溝通的意圖。例如:要求 RemoteSystem 執行 remote launch 或 remote app service 都需要利用它來建立連線。
- RemoteLauncher
啟動與遠端設備上的指定 URI 關聯的預設應用程式。 常見都是設定 URI,如果需要比較複雜的使用可以參考 LaunchUriAsync(RemoteSystemConnectionRequest, Uri, RemoteLauncherOptions):可以設定 FallbackUri 或 PreferredAppIds (給于package family names) 啓動指定的 App。
上面的範例可以到 26-RemoteSystemSample 下載來使用。
詳細説明可以參考 Windows.System.RemoteSystems Namespace ,或是範例 Remote Systems UWP sample。
======
這篇的介紹是以 Windows 設備為主,如果想要 Windows 操作其他設備,可以參考微軟提供的 SDK:Announcing Project Rome Android SDK 與 Announcing Project Rome iOS SDK。
希望對大家有所幫助。
References:
- Project Rome
- Announcing Project Rome Android SDK
- Announcing Project Rome iOS SDK
- Microsoft/project-rome
- Remote Systems sample
- App services sample
- Jump list customization sample
- Connected apps and devices (Project Rome)
- Discover remote devices
- Launch an app on a remote device
- Communicate with a remote app service
- Connect devices through remote sessions
- Remote Systems sample
- Sign-in Microsoft Account & Azure AD users in a single app
- Cross-device experiences with Project Rome
- Project Rome for Android (preview release)
- Announcing Project Rome Android SDK
- 建立和取用 App 服務