[Preview] ToastHistoryChangeTracker


COMING SOON: You can finally know what notifications your user dismissed. The ToastHistoryChangeTracker library works with all versions of Windows 10, and is a library that you can include in any UWP app, regardless of which SDK you are targeting!

Apps can use the ToastHistoryChangeTracker to keep their Live Tile and Toast notifications in sync. For example, you can use the tracker to know that "X notification was dismissed, so remove that from the Live Tile". But you also can use the tracker for numerous other scenarios that we haven't thought of!

At its core, the tracker lets you know...

  • Which notification was added via push
  • Which notification was removed, for following events...
    • Dismissed by the user
    • Clicked on by the user
    • Expired based on expiration time
    • Removed via push
  • Additional information about the notification, like...
    • Tag
    • Group
    • DateAdded
    • DateRemoved
    • AdditionalData (extra data you can provide when sending)
    • Payload (the original XML payload)
    • PayloadArguments (the launch args from the XML payload)

Adding the NuGet package

TODO

Add namespace declaration

Our helper library, and the associated extension methods, are located in Microsoft.Toolkit.Uwp.Notifications.

using Microsoft.Toolkit.Uwp.Notifications;

Enabling change tracking

To start change tracking, you need to make a call to EnableAsync. We recommend doing this in your initial application startup code. The call only needs to be made once, but there is no harm in calling it subsequent times.

EnableAsync will set up the database to track which notifications are added and removed. Without calling EnableAsync, you won't be able to receive any changes.


protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
    await ToastHistoryChangeTracker.Current.EnableAsync();

    ... (your normal OnLaunched code)
}

Sending a Toast notification

When sending local Toast notifications (or clearing or removing), you have to use our special *Enhanced extension methods. These methods will call into our change tracking database, saving the Toast notification, so that later on we can determine whether that Toast notification was dismissed.

These methods are async since they write to the database before sending. Therefore, these method calls should be awaited to ensure that they complete. For example, if you're sending a Toast notification from a background task and you do not await the ShowEnhanced method, your background task will reach the end of the code and potentially will exit before the notification is actually saved and sent.

// Make sure you added the namespace,
// otherwise, the *Enhanced extension methods won't be available
using Microsoft.Toolkit.Uwp.Notifications;

...

// Construct your Toast content like normal
ToastContent content = new ToastContent()
{
    ... (omitted)
};
 
// Create the Toast notification like normal
var toast = new ToastNotification(content.GetXml());

// And then send using the *Enhanced method
await ToastNotificationManager.CreateToastNotifier().ShowEnhanced(toast);

Background task for receiving changes

In order to execute code when a user dismisses your notification, you need to use the ToastNotificationHistoryChangedTrigger, which is part of the Windows UWP platform since the first version of Windows 10.

This background task trigger is simply a shoulder tap, to say that "something has changed". You need to use our tracker library to determine *what* has changed.

We recommend registering your background tasks at your initial app initialization, for example, inside the constructor of your App.xaml.cs class.

// Request access to execute background tasks
await BackgroundExecutionManager.RequestAccessAsync();

// If our task isn't already registered
if (!BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals("ToastHistoryChange")))
{
    // Register it (using the single process model)
    BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
    {
        Name = "ToastHistoryChange"
    };
    builder.SetTrigger(new ToastNotificationHistoryChangedTrigger());
    builder.Register();
}

Getting the changes

At any point in time, you can use the tracker to find out what has changed. You will most likely want to do this when your background task is triggered, but you also might want to obtain changes at other points in time.

To find out what has changed, you first will obtain a reader, and then you will read the changes, which provides you with a list of changes. You can check the ChangeType to find out what change has occurred on each item.

Once you are done processing the changes, call AcceptChangesAsync to accept the changes, so that you do not see these changes in the future.

To get additional new changes, you have to obtain a new change reader. The existing change reader will only return the changes that happened at the moment you called GetChangeReaderAsync.

The following snippet is executed in our background task, and it checks whether a conversation Toast was removed. If so, it finds the corresponding conversation and marks it as dismissed. At the end, if we had changes, we update the Live Tile, so that it only displays messages from conversations that haven't been dismissed.

// Get the change reader
var reader = await ToastHistoryChangeTracker.Current.GetChangeReaderAsync();

// And get the changes
var changes = await reader.ReadChangesAsync();

bool madeChanges = false;

// Look through the changes
foreach (var c in changes)
{
    // If one of our conversation toasts was removed
    if (c.ChangeType == ToastHistoryChangeType.Removed && c.Group.Equals("conversations"))
    {
        // Find the conversation corresponding to this toast
        int convId = int.Parse(c.Tag);
        var conv = viewModel.Conversations.FirstOrDefault(i => i.Id == convId);

        if (conv != null)
        {
            // Mark that the notification was dismissed
            conv.MarkNotificationDismissed();
            madeChanges = true;
        }
    }
}

// Accept these changes since we've processed them
await reader.AcceptChangesAsync();

if (madeChanges)
{
    // Update our Live Tile if changes occurred
    TileHelper.Update();
}
Skip to main content