用户活动是表示应用程序中用户任务的数据构造。 通过它们,可以保存当前任务的快照,以便稍后继续。 Windows 时间线功能向 Windows 用户提供其所有最近活动的可滚动列表,这些活动表示为带有文本和图形的卡片。 有关一般用户活动的详细信息,请参阅 继续用户活动,即便跨设备进行。 有关何时创建或更新活动的建议,请参阅 用户活动最佳做法 指南。
借助 Project Rome SDK,iOS 应用不仅可以发布用户活动以在 Windows 功能(如时间线)中使用,还可以充当终结点并像时间线一样将活动读回用户。 这允许跨设备应用完全超越其平台,并呈现遵循用户而不是设备的体验。
Project Rome 的功能受名为“已连接设备平台”的基础平台支持。 本指南提供了开始使用连接设备平台的必要步骤,然后说明如何使用平台实现用户活动相关功能。
以下步骤将引用 GitHub 上提供的 Project Rome iOS 示例应用中 的代码。
有关与这些方案相关的参考文档的链接,请参阅 API 参考 页。
设置连接设备平台和通知
注册应用
Project Rome SDK 的几乎所有功能都需要Microsoft帐户(MSA)或 Azure Active Directory (AAD) 身份验证(异常是附近的共享 API)。 如果还没有 MSA 并希望使用 MSA,请注册 account.microsoft.com。
注释
设备中继 API 不支持 Azure Active Directory (AAD) 帐户。
使用所选的身份验证方法,必须按照应用程序 注册门户上的说明将应用注册到Microsoft。 如果没有Microsoft开发人员帐户,则需要创建一个。
使用 MSA 注册应用时,应会收到客户端 ID 字符串。 保存此项供以后使用。 这将允许你的应用访问Microsoft的联网设备平台资源。 如果使用 AAD,请参阅 Azure Active Directory 身份验证库 ,获取客户端 ID 字符串的说明。
添加 SDK
将连接的设备平台添加到 iOS 应用的最简单方法是使用 CocoaPods 依赖项管理器。 转到 iOS 项目的 Podfile 并插入以下条目:
platform :ios, "10.0"
workspace 'iOSSample'
target 'iOSSample' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
pod 'ProjectRomeSdk'
# Pods for iOSSample
注释
若要使用 CocoaPod,必须在项目中使用 .xcworkspace 文件。
设置身份验证和帐户管理
连接设备平台要求在注册过程中使用有效的 OAuth 令牌。 可以使用首选方法来生成和管理 OAuth 令牌。 但是,为了帮助开发人员开始使用该平台,我们已将身份验证提供程序包含在 iOS 示例应用中 ,可用于在应用中生成和管理刷新令牌。
如果不使用提供的代码,则需要自行实现 MCDConnectedDevicesAccountManager 接口。
如果使用 MSA,请在登录请求中包含以下范围: "wl.offline_access"
、、 "ccs.ReadWrite"
、 "dds.read"
、 "dds.register"
、 "wns.connect"
、 "asimovrome.telemetry"
和 "https://activity.windows.com/UserActivity.ReadWrite.CreatedByApp"
。
注释
设备中继 API 不支持 Azure Active Directory (AAD) 帐户。
如果使用 AAD 帐户,则需要请求以下访问对象:"https://cdpcs.access.microsoft.com"
、"https://cs.dds.microsoft.com"
、"https://wns.windows.com/"
和"https://activity.microsoft.com"
。
无论是否使用提供的 MCDConnectedDevicesAccountManager 实现,如果使用 AAD,则需要在 Azure 门户中的应用注册中指定以下权限(portal.azure.com > Azure Active Directory > 应用注册):
- Microsoft活动流服务
- 传递和修改此应用的用户通知
- 将应用活动读取和写入到用户的活动源
- Windows 通知服务
- 将设备连接到 Windows 通知服务
- Microsoft设备目录服务
- 查看设备列表
- 添加到设备和应用列表
- Microsoft命令服务
- 与用户设备通信
- 读取用户设备
为推送通知注册应用程序
向 Apple 注册应用程序以获取 Apple 推送通知 支持。 请务必记下收到的发送方 ID 和服务器密钥,因为稍后将需要它们。
注册后,必须在应用中将推送通知功能与连接的设备平台相关联。
self.notificationRegistration = [[MCDConnectedDevicesNotificationRegistration alloc] init];
if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
{
self.notificationRegistration.type = MCDNotificationTypeAPN;
}
else
{
self.notificationRegistration.type = MCDNotificationTypePolling;
}
self.notificationRegistration.appId = [[NSBundle mainBundle] bundleIdentifier];
self.notificationRegistration.appDisplayName = (NSString*)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
self.notificationRegistration.token = deviceToken;
self.isRegisteredWithToken = YES;
在 Microsoft Windows 开发人员中心注册应用,以获取跨设备体验
警告
仅当想要使用 Project Rome 功能访问非 Windows 设备的数据或发出请求时,才需要执行此步骤。 如果仅面向 Windows 设备,则无需完成此步骤。
为 Microsoft开发人员仪表板的跨设备体验功能注册应用。 这是与上述 MSA 和 AAD 应用注册不同的过程。 此过程的主要目标是将特定平台的应用标识映射到由已连接设备平台识别的跨平台应用标识。 此步骤还将使用与应用使用的移动平台相对应的本机推送通知服务启用发送通知。 对于 iOS,它允许通过 APNS – Apple 推送通知服务将通知发送到 iOS 应用终结点。
转到开发人员中心仪表板,从左侧导航窗格中导航到跨设备体验,然后选择配置新的跨设备应用。
开发人员中心加入过程需要执行以下步骤:
选择支持的平台 – 选择您的应用将覆盖并支持跨设备体验的平台。 对于 Graph 通知集成,可以根据所使用的平台从 Windows、Android 和/或 iOS 中进行选择。
提供应用 ID - 为所使用的每个平台提供应用 ID。 对于 iOS 应用,这是创建项目时分配给应用的包名称。 请注意,可以为每个平台添加不同的 ID(最多 10 个),在这种情况下,你可能有多个版本的同一应用甚至不同的应用,他们希望能够接收针对同一用户的应用服务器发送的相同通知。
提供或选择在上述 MSA/AAD 应用注册步骤中获取的 MSA 和/或 AAD 应用注册中的应用 ID。
提供与应用相关的本机通知平台(即适用于 Windows 的 WNS、适用于 Android 的 FCM 和/或适用于 iOS 的 APNS)的凭据,以便在发布面向用户的通知时从应用服务器传递通知。
最后,验证跨设备应用域,确保应用拥有该域的所有权,并将其用作应用的跨设备标识。
使用平台
创建平台的实例
要开始使用,只需实例化该平台即可。
MCDConnectedDevicesPlatform* platform = [MCDConnectedDevicesPlatform new];
订阅 MCDConnectedDevicesAccountManager 服务
平台需要经过身份验证的用户才能访问平台。 需要订阅 MCDConnectedDevicesAccountManager 事件,以确保使用有效的帐户。
[MCDConnectedDevicesPlatform* platform.accountManager.accessTokenRequested
subscribe:^(MCDConnectedDevicesAccountManager* _Nonnull manager __unused,
MCDConnectedDevicesAccessTokenRequestedEventArgs* _Nonnull request __unused) {
// Get access token
}
[MCDConnectedDevicesPlatform* platform.platform.accountManager.accessTokenInvalidated
subscribe:^(MCDConnectedDevicesAccountManager* _Nonnull manager __unused,
MCDConnectedDevicesAccessTokenInvalidatedEventArgs* _Nonnull request) {
// Refresh and renew existing access token
}
订阅 MCDConnectedDevicesNotificationRegistrationManager
同样,平台使用通知在设备之间传递命令。 因此,必须订阅 MCDConnectedDevicesNotificationRegistrationManager 事件,以确保云注册状态对正在使用的帐户有效。 使用 MCDConnectedDevicesNotificationRegistrationState 验证状态
[MCDConnectedDevicesPlatform* platform.notificationRegistrationManager.notificationRegistrationStateChanged
subscribe:^(MCDConnectedDevicesNotificationRegistrationManager* manager __unused,
MCDConnectedDevicesNotificationRegistrationStateChangedEventArgs* args __unused) {
// Check state using MCDConnectedDevicesNotificationRegistrationState enum
}
启动平台
现在,平台已初始化,事件处理程序已准备就绪,可以开始发现远程系统设备。
[MCDConnectedDevicesPlatform* platform start];
检索应用已知的用户帐户
请务必确保应用已知的用户帐户列表与 MCDConnectedDevicesAccountManager 正确同步。
使用 MCDConnectedDevicesAccountManager.addAccountAsync 添加新的用户帐户。
[MCDConnectedDevicesPlatform* platform.accountManager
addAccountAsync:self.mcdAccount
callback:^(MCDConnectedDevicesAddAccountResult* _Nonnull result, NSError* _Nullable error) {
// Check state using **MCDConnectedDevicesAccountAddedStatus** enum
}
若要删除无效帐户,可以使用 MCDConnectedDevicesAccountManager.removeAccountAsync
[MCDConnectedDevicesPlatform* platform.accountManager
removeAccountAsync:existingAccount
callback:^(MCDConnectedDevicesRemoveAccountResult* _Nonnull result __unused, NSError* _Nullable error) {
// Remove invalid user account
}
初始化用户活动通道
若要在应用中实现用户活动功能,首先需要通过创建 MCDUserActivityChannel 来初始化用户活动源。 应像上面的平台初始化步骤一样对待:每当应用到达前台(但在平台初始化之前)时,都应检查并可能重新执行该步骤。
此步骤需要一个已登录用户帐户。 如上所述,可以使用身份验证提供程序示例中的类轻松获取 MCDUserAccount(s)。 您还需要通过 Microsoft 开发者仪表板注册获得的跨平台应用 ID。 示例应用中的以下方法初始化 MCDUserActivityChannel。
示例应用中的以下方法初始化 MCDUserActivityChannel。
// Get a UserActivity channel, getting the default channel
NSLog(@"Creating UserActivityChannel");
NSArray<MCDUserAccount*>* accounts = [[AppDataSource sharedInstance].accountProvider getUserAccounts];
MCDUserDataFeed* userDataFeed = [MCDUserDataFeed userDataFeedForAccount:accounts[0]
platform:[AppDataSource sharedInstance].platform
activitySourceHost:CROSS_PLATFORM_APP_ID];
NSArray<MCDSyncScope*>* syncScopes = @[ [MCDUserActivityChannel syncScope] ];
[userDataFeed addSyncScopes:syncScopes];
self.channel = [MCDUserActivityChannel userActivityChannelWithUserDataFeed:userDataFeed];
}
else
{
NSLog(@"Must have an active account to publish activities!");
self.createActivityStatusField.text = @"Need to be logged in!";
}
此时,应在通道中具有 MCDUserActivityChannel 引用。
创建和发布用户活动
以下示例代码演示如何创建新的 MCDUserActivity 实例。
- (IBAction)createActivityButton:(id)sender
{
// Step #2: Create a UserActivity
[self.channel getOrCreateUserActivityAsync:[[NSUUID UUID] UUIDString]
completion:^(MCDUserActivity* activity, NSError* error) {
if (error)
{
NSLog(@"%@", error);
self.createActivityStatusField.text = @"Error creating activity!";
}
else if (!activity)
{
NSLog(@"No activity created!");
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
self.activity = activity;
// Create an activityId so the app knows so you know how to get back
self.activityId.text = activity.activityId;
self.createActivityStatusField.text = @"Created by iOSSample";
// Create a deep link so the app can get right back to where it was
self.activationUri.text = @"roman-app:";
self.createActivityStatusField.text = @"Activity created";
});
}
}];
}
在后续方法中,会在发布活动之前设置 MCDUserActivity 的视觉信息。 激活 URI 将决定在活动被激活时采取什么操作(例如,在时间线中选择活动时)。 查看活动时(例如,在 Windows 时间线中),显示文本将显示在其他设备上。 iconUri 是指向图标图像的 Web 链接。
- (IBAction)publishActivityButton:(id)sender
{
self.createActivityStatusField.text = @"Saving";
self.activity.activationUri = self.activationUri.text;
// Set the display text for your activity.
self.activity.visualElements.displayText = @"Visual Element, like an Adaptive Card";
self.activity.visualElements.attribution.iconUri = @"https://www.microsoft.com/favicon.ico";
// ...
填充 MCDUserActivity 后,将执行发布操作。
// ...
// Publish the Activity
[self.activity saveAsync:^(NSError* error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error)
{
NSLog(@"%@", error);
self.createActivityStatusField.text = @"Error saving activity";
}
else
{
self.createActivityStatusField.text = @"Saved successfully!";
[self.sessionButton setTitle:@"Start Session" forState:normal];
}
});
}];
}
mActivityId = UUID.randomUUID().toString();
mDisplayText = "Created by OneSDK Sample App";
mActivationUri = "http://contoso.com");
小窍门
除了上述属性,还可以配置许多其他功能。 有关可自定义 UserActivity 的不同方式的完整了解,请参阅 MCDUserActivity、 MCDUserActivityVisualElements 和 MCDUserActivityAttribution 类。 有关如何设计用户活动的详细建议,请参阅 用户活动最佳做法 指南。
更新现有用户活动
如果你有现有活动并希望更新其信息(如果存在新的参与、更改的页面等),则可以使用 MCDUserActivitySession 执行此作。
创建会话后,应用可以对 UserActivity 的属性进行任何所需的更改。 完成更改后,请关闭会话。
示例应用中的以下方法打开和关闭会话。
- (IBAction)manageSessionButton:(id)sender
{
// Start a new a session for the UserActivity
if (self.session == nil)
{
self.session = [self.activity createSession];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"UserActivitySession has started %@", self.session);
self.session = [self.activity createSession];
dispatch_async(dispatch_get_main_queue(), ^{ [self.sessionButton setTitle:@"Stop Session" forState:normal]; });
});
}
else
{
// Stop the UserActivitySession
[self.session close];
self.session = nil;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"UserActivitySession has stopped %@", self.session);
[self.sessionButton setTitle:@"Start Session" forState:normal];
});
}
}
可以将 MCDUserActivitySession 视作创建 MCDUserActivitySessionHistoryItem 的一种途径(下一部分所述)。 每次用户移动到新页面时,都无需创建新的 MCDUserActivity ,只需为每个页面创建新会话即可。 这将提供更直观有序的活动阅读体验。
读取用户活动
你的应用可以读取用户活动并将其呈现给用户,就像 Windows 时间线功能一样。 若要设置用户活动读取,请使用前面相同的 MCDUserActivityChannel 实例。 此实例可以公开 MCDUserActivitySessionHistoryItem 实例,这些实例表示用户在特定时间段内在特定活动中参与的活动。
- (IBAction)readActivityButton:(id)sender
{
// Read/sync your UserActivities
[self.channel getRecentUserActivitiesAsync:(NSInteger)5
completion:^(NSArray<MCDUserActivitySessionHistoryItem*>* _Nonnull result, NSError* _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error)
{
self.readActivityStatusField.text = @"Error reading activity!";
}
else if (result.count == 0)
{
self.readActivityStatusField.text = @"Read completed, no activities returned";
}
else
{
self.readActivityStatusField.text = @"Read completed!";
MCDUserActivitySessionHistoryItem* item = result.firstObject;
self.activityList.text = item.userActivity.activityId;
self.activityDisplay.text = item.userActivity.visualElements.displayText;
}
});
}];
}
现在,你的应用应包含 MCDUserActivitySessionHistoryItem的填充列表。 其中每个都可以提供基础 MCDUserActivity(有关详细信息,请参阅 MCDUserActivitySessionHistoryItem),然后可以向用户显示。