





Great applications help users do great things — enabling a wide range of creative, productivity and entertainment scenarios. Returning to activities can be a challenge, especially when a person wants to continue those activities across multiple devices. By writing User Activities , application developers have a new tool to get users back into their application.

In this article, you’ll learn how to drive application re-engagement by writing great User Activities into the Microsoft Graph with their UWP applications. This article is also a companion to the Microsoft Connect(); session: Engaging with your customers on any platform using the Microsoft Graph, Activity Feed, and Adaptive Cards.

User Activities and Timeline

Starting in Windows Insider Preview build 17056 or higher, User Activities generated by your application appear in Timeline. By writing User Activities into the Microsoft Graph, you can express specific content within your application as a destination which is showcased in Windows, and accessible on your iOS and Android devices.

Each User Activity represents a single destination within your app: such as a TV show, document or your current campaign in a game. When you engage with that activity (by creating an Activity Session), the system creates a history record indicating the start and end time for that activity. As you re-engage with that User Activity over time, multiple History Records will be recorded for a single User Activity. Here’s how to get started:

Adding UserActivities to your app

UserActivities are the unit of user engagement in Windows, and they consist of three components: a deep-link, visuals and content metadata.

The Activation Deep Link is a URI that can be passed back to an application or experience in order to resume the application with specific context. Typically, these links take the form of protocol handler for a scheme (e.g. “my-app://page2?action=edit”) or an AppUriHandlers (e.g. http://constoso.com/page2?action=edit). Visuals are a set of properties that allow users to visually identify an activity, for example: title, description, or Adaptive Card elements. Finally, Content Metadata is metadata for the content of the of activity that can be used to group and retrieve activities under a specific context. Often, this takes the form of http://schema.org data.

In order to integrate UserActivities with your application, you need to:

Generate UserActivity objects when your user’s context changes within an application (page navigation, new game, etc.) Populate UserActivity objects with the minimum set of required fields: ActivityId, ActivationUri, DisplayText Add a custom scheme handler to your application so it can be re-activated by your UserActivities

UserActivities can be integrated into an application with just a few lines of code:

UserActivitySession _currentActivity; private async Task GenerateActivityAsync() { //Get the default UserActivityChannel and query it for our UserActivity. If the activity doesn't exist, one is created. UserActivityChannel channel = UserActivityChannel.GetDefault(); UserActivity userActivity = await channel.GetOrCreateUserActivityAsync("MainPage"); //Populate required properties userActivity.VisualElements.DisplayText = "Hello Activities"; userActivity.ActivationUri = new Uri("my-app://page2?action=edit"); //Save await userActivity.SaveAsync(); //save the new metadata //Dispose of any current UserActivitySession, and create a new one. _currentActivity?.Dispose(); _currentActivity = userActivity.CreateSession(); }

The first line in the GenerateActivityAsync() method gets a user’s UserActivityChannel. This is the feed that this app’s activities will be published to. The next line queries that channel of an activity called “MainPage”

Your application should name activities in such a way that same ID is generated each time the user is in a particular location in the app. For example, if your application is page-based, use an identifier for the page, if it’s document based, use the name of the doc (or a hash of the name).

If there is an existing activity in the feed with the same ID, that activity will be return from the channel (with the UserActivity object State property set to Published). If there is no activity with that name, and new activity with State set to New.

Activities are scoped to your app, there is no need to worry about your activity ID colliding with IDs from other applications

After getting or creating the activity the next lines of code specify the other two required fields: the DisplayText and the ActivationUri.

Next, save the UserActivity metadata, by calling SaveAsync(), and finally CreateSession(). That last method returns a UserActivitySession object that we can use to manage when the user is actually engaged with the UserActivity. For example, we should call Dispose() on the UserActivitySession when the user leaves the page. In the example above, we also call Dispose() on _currentActivity right before we call CreateSession(). This is because we made _currentActivity a member field of our page, and we want to stop any existing activity before we start the new one (the ‘?’ is an inline null-check).

Since, in this case, our ActivationUri is a custom scheme, we also need to register the Protocol in the application manifest. This can be done in the Package.appmanifest XML file, or using the designer, as shown below. Double-click the Package.appmanifest file to launch the designer, select the Declarations tab and add a Protocol definition. The only property that needs to be filled out, for now, is Name. It should match the URI we specified above hello-activities.

Now we need to write some code to tell the application what to do when it’s been activated via a protocol. In this case, we’ll override the OnActivated method in App.xaml.cs to pass the URI on to our MainPage:

protected override void OnActivated(IActivatedEventArgs e) { LoadFrame(e); if (e.Kind == ActivationKind.Protocol) { var uriArgs = e as ProtocolActivatedEventArgs; if (uriArgs != null) { Frame rootFrame = Window.Current.Content as Frame; if (uriArgs.Host == "page2”) { rootFrame.Navigate(typeof(SecondaryPage), uriArgs) } } } Window.Current.Activate(); }

Use Adaptive Cards to Improve the Timeline Experience

User Activities will appear in Cortana and Timeline experiences. When activities appear in Timeline, we display them using the Adaptive Card framework. If you do not provide an adaptive card for each activity, Timeline will automatically create a simple activity card based on your application name and icon, the required Title field and optional Description field. Below is an example Adaptive Card payload and the card it produces.

{ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "backgroundImage": "https://46c4ts1tskv22sdav81j9c69-wpengine.netdna-ssl.com/wp-content/uploads/2017/11/eb5d872c743f8f54b957ff3f5ef3066b.jpg", "body": [ { "type": "Container", "items": [ { "type": "TextBlock", "text": "Windows Blog", "weight": "bolder", "size": "large", "wrap": true, "maxLines": 3 }, { "type": "TextBlock", "text": "Training Haiti’s radiologists: St. Louis doctor takes her teaching global", "size": "default", "wrap": true, "maxLines": 3 } ] } ] }

Adaptive Cards can be added to the UserActivity object by passing a JSON string to the AdaptiveCardBuilder and setting the UserActivity.VisualElements.Content property:

activity.VisualElements.Content = AdaptiveCardBuilder.CreateAdaptiveCardFromJson(cardText);

Cross-platform and Service-to-service integration

If your application has a cross-platform presence (for example on Android and iOS), or maintains user state in the cloud, you can publish UserActivities via integration with Microsoft Graph.

Once your application or service is authenticated with a Microsoft Account, it is two simple REST calls to generate Activity and History objects, using the same data as described above.

Summary

In this blog post, we learned how to use the UserActivity API to make your application show up in Timeline and Cortana, but there is much more you can do: