Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New push toast prototype #266

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
#include <Microsoft.UI.Xaml.Window.h>
#include <winrt/Microsoft.Windows.AppLifecycle.h>
#include <winrt/Microsoft.Windows.AppNotifications.h>
#include <winrt/Windows.ApplicationModel.Background.h>

namespace winrt
{
using namespace Windows::Foundation;
using namespace Microsoft::UI::Xaml;
using namespace winrt::Microsoft::Windows::AppLifecycle;
using namespace winrt::Microsoft::Windows::AppNotifications;
using namespace Microsoft::Windows::AppLifecycle;
using namespace Microsoft::Windows::AppNotifications;
using namespace Microsoft::Windows::PushNotifications;
}

// NotificationManager is responsible for registering and unregistering the Sample for App Notifications as well as
Expand Down Expand Up @@ -87,12 +89,39 @@ namespace winrt::CppUnpackagedAppNotifications::implementation
winrt::AppActivationArguments activationArgs{ currentInstance.GetActivatedEventArgs() };
if (activationArgs)
{
winrt::ExtendedActivationKind extendedKind{ activationArgs.Kind() };
if (extendedKind == winrt::Microsoft::Windows::AppLifecycle::ExtendedActivationKind::AppNotification)
switch (activationArgs.Kind())
{
// When it is activated from a push notification, the sample only displays the notification.
// It doesn’t register for foreground activation of perform any other actions
// because background activation is meant to let app perform only small tasks in order to preserve battery life.
case ExtendedActivationKind::Push:
{
winrt::PushNotificationReceivedEventArgs pushArgs{ activationArgs.Data().as<winrt::PushNotificationReceivedEventArgs>() };

// Call GetDeferral to ensure that code runs in low power
auto deferral{ pushArgs.GetDeferral() };

auto payload{ pushArgs.Payload() };

// Do stuff to process the raw notification payload
std::string payloadString(payload.begin(), payload.end());
//std::cout << "\nPush notification content received in the BACKGROUND: " << payloadString.c_str() << std::endl;
//std::cout << "\nPress 'Enter' to exit the App." << std::endl;

// Call Complete on the deferral when finished processing the payload.
// This removes the override that kept the app running even when the system was in a low power mode.
deferral.Complete();
//std::cin.ignore();
}
break;

case winrt::Microsoft::Windows::AppLifecycle::ExtendedActivationKind::AppNotification:
{
winrt::AppNotificationActivatedEventArgs notificationActivatedEventArgs{ activationArgs.Data().as<winrt::AppNotificationActivatedEventArgs>() };
g_notificationManager.ProcessLaunchActivationArgs(notificationActivatedEventArgs);
}
break;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
</ClInclude>
<ClInclude Include="Notifications\Common.h" />
<ClInclude Include="Notifications\NotificationManager.h" />
<ClInclude Include="Notifications\PushToastWithAvatar.h" />
<ClInclude Include="Notifications\ToastWithAvatar.h" />
<ClInclude Include="Notifications\ToastWithTextBox.h" />
<ClInclude Include="NotifyUser.h" />
Expand All @@ -139,6 +140,9 @@
<DependentUpon>Scenario2_ToastWithTextBox.xaml</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="Scenario3_PushToastWithAvatar.xaml.h">
<DependentUpon>Scenario3_PushToastWithAvatar.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="SettingsPage.xaml.h">
<DependentUpon>SettingsPage.xaml</DependentUpon>
<SubType>Code</SubType>
Expand All @@ -156,6 +160,7 @@
<Page Include="Scenario2_ToastWithTextBox.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="Scenario3_PushToastWithAvatar.xaml" />
<Page Include="SettingsPage.xaml">
<SubType>Designer</SubType>
</Page>
Expand All @@ -169,6 +174,7 @@
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="Notifications\NotificationManager.cpp" />
<ClCompile Include="Notifications\PushToastWithAvatar.cpp" />
<ClCompile Include="Notifications\ToastWithAvatar.cpp" />
<ClCompile Include="Notifications\ToastWithTextBox.cpp" />
<ClCompile Include="NotifyUser.cpp" />
Expand All @@ -191,6 +197,9 @@
<DependentUpon>Scenario2_ToastWithTextBox.xaml</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="Scenario3_PushToastWithAvatar.xaml.cpp">
<DependentUpon>Scenario3_PushToastWithAvatar.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="SettingsPage.xaml.cpp">
<DependentUpon>SettingsPage.xaml</DependentUpon>
<SubType>Code</SubType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<Page Include="Scenario1_ToastWithAvatar.xaml" />
<Page Include="Scenario2_ToastWithTextBox.xaml" />
<Page Include="Styles.xaml" />
<Page Include="Scenario3_PushToastWithAvatar.xaml" />
</ItemGroup>
<ItemGroup>
<Midl Include="Project.idl" />
Expand All @@ -28,6 +29,9 @@
<ClCompile Include="Notifications\ToastWithTextBox.cpp">
<Filter>Notifications</Filter>
</ClCompile>
<ClCompile Include="Notifications\PushToastWithAvatar.cpp">
<Filter>Notifications</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
Expand All @@ -45,6 +49,9 @@
<ClInclude Include="Notifications\ToastWithTextBox.h">
<Filter>Notifications</Filter>
</ClInclude>
<ClInclude Include="Notifications\PushToastWithAvatar.h">
<Filter>Notifications</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="Assets\Wide310x150Logo.png">
Expand Down Expand Up @@ -80,4 +87,7 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace winrt
{
using namespace Microsoft::UI::Xaml::Controls;
using namespace Microsoft::Windows::AppNotifications;
using namespace Microsoft::Windows::PushNotifications;
using namespace CppUnpackagedAppNotifications::implementation;
}

Expand All @@ -39,12 +40,29 @@ NotificationManager::~NotificationManager()

void NotificationManager::Init()
{
auto notificationManager{ winrt::AppNotificationManager::Default() };
auto pushNotificationManager = winrt::PushNotificationManager::Default();

if (pushNotificationManager.IsSupported())
{
// Setup an event handler, so we can receive notifications in the foreground while the app is running.
SubscribeForegroundEventHandler();

pushNotificationManager.Register();
}
else
{
// Here, the app should handle the case where push notifications are not supported, for example:
// - maintain its own persistent connection with an App Service or
// - use polling over a scheduled interval to synchronize the client.
//std::cout << "\nPush Notifications aren't supported." << std::endl;
}

auto appNotificationManager{ winrt::AppNotificationManager::Default() };

// To ensure all Notification handling happens in this process instance, register for
// NotificationInvoked before calling Register(). Without this a new process will
// be launched to handle the notification.
const auto token{ notificationManager.NotificationInvoked([&](const auto&, winrt::AppNotificationActivatedEventArgs const& notificationActivatedEventArgs)
const auto token{ appNotificationManager.NotificationInvoked([&](const auto&, winrt::AppNotificationActivatedEventArgs const& notificationActivatedEventArgs)
{
NotifyUser::NotificationReceived();

Expand Down Expand Up @@ -84,3 +102,14 @@ bool NotificationManager::DispatchNotification(winrt::AppNotificationActivatedEv
return false; // No scenario specified in the notification
}
}

void NotificationManager::SubscribeForegroundEventHandler()
{
winrt::event_token token{ winrt::PushNotificationManager::Default().PushReceived([](auto const&, winrt::PushNotificationReceivedEventArgs const& args)
{
auto payload{ args.Payload() };

std::string payloadString(payload.begin(), payload.end());
//std::cout << "\nPush notification content received in the FOREGROUND: " << payloadString << std::endl;
}) };
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#pragma once
#include <winrt/Microsoft.Windows.AppNotifications.h>
#include <winrt/Microsoft.Windows.PushNotifications.h>

class NotificationManager
{
Expand All @@ -15,6 +16,7 @@ class NotificationManager

private:
bool DispatchNotification(winrt::Microsoft::Windows::AppNotifications::AppNotificationActivatedEventArgs const& notificationActivatedEventArgs);
void SubscribeForegroundEventHandler();

bool m_isRegistered;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include "pch.h"
#include "PushToastWithAvatar.h"
#include "Common.h"
#include <winrt/Microsoft.Windows.AppNotifications.h>
#include <winrt/Microsoft.Windows.AppNotifications.Builder.h>
#include <winrt/Windows.Foundation.h>
#include "App.xaml.h"
#include "MainPage.xaml.h"

namespace winrt
{
using namespace Microsoft::Windows::AppNotifications;
using namespace Microsoft::Windows::AppNotifications::Builder;
using namespace Microsoft::Windows::PushNotifications;
using namespace Windows::Foundation;
using namespace CppUnpackagedAppNotifications::implementation;
}

const wchar_t* PushToastWithAvatar::ScenarioName{ L"Push / Local Toast with Avatar Image" };

// To obtain your Azure AppId, follow "Configure your app's identity in Azure Active Directory
// at https://docs.microsoft.com/en-us/windows/apps/windows-app-sdk/notifications/push/push-quickstart
const winrt::guid PushToastWithAvatar::RemoteId{ "00000000-0000-0000-0000-000000000000" }; // Replace this with your own Azure AppId

bool PushToastWithAvatar::SendToast()
{
auto appNotification{ winrt::AppNotificationBuilder()
.AddArgument(L"action", L"ToastClick")
.AddArgument(Common::scenarioTag, std::to_wstring(PushToastWithAvatar::ScenarioId))
.SetAppLogoOverride(winrt::Windows::Foundation::Uri(L"file://" + winrt::App::GetFullPathToAsset(L"Square150x150Logo.png")), winrt::AppNotificationImageCrop::Circle)
.AddText(ScenarioName)
.AddText(L"This is an example message using XML")
.AddButton(winrt::AppNotificationButton(L"Open App")
.AddArgument(L"action", L"OpenApp")
.AddArgument(Common::scenarioTag, std::to_wstring(PushToastWithAvatar::ScenarioId)))
.BuildNotification() };

winrt::AppNotificationManager::Default().Show(appNotification);

return appNotification.Id() != 0; // return true (indicating success) if the toast was sent (if it has an Id)
}

void PushToastWithAvatar::NotificationReceived(winrt::Microsoft::Windows::AppNotifications::AppNotificationActivatedEventArgs const& notificationActivatedEventArgs)
{
winrt::CppUnpackagedAppNotifications::Notification notification{};
notification.Originator = ScenarioName;
notification.Action = notificationActivatedEventArgs.Arguments().Lookup(L"action");
winrt::MainPage::Current().NotificationReceived(notification);
winrt::App::ToForeground();
}

winrt::Microsoft::Windows::PushNotifications::PushNotificationChannel PushToastWithAvatar::RequestChannel()
{
auto task{ RequestChannelAsync() };
if (task.wait_for(std::chrono::minutes(5)) != winrt::AsyncStatus::Completed)
{
task.Cancel();
return nullptr;
}

auto result{ task.GetResults() };
return result;
}

winrt::Windows::Foundation::IAsyncOperation<winrt::PushNotificationChannel> PushToastWithAvatar::RequestChannelAsync()
{
auto channelOperation{ winrt::PushNotificationManager::Default().CreateChannelAsync(RemoteId) };

// Setup the in-progress event handler
channelOperation.Progress(
[](auto&& /*sender*/, auto&& args)
{
if (args.status == winrt::PushNotificationChannelStatus::InProgress)
{
// This is basically a noop since it isn't really an error state
//std::cout << "\nWNS Channel URI request is in progress." << std::endl;
}
else if (args.status == winrt::PushNotificationChannelStatus::InProgressRetry)
{
//LOG_HR_MSG(
// args.extendedError,
// "The WNS Channel URI request is in back-off retry mode because of a retryable error! Expect delays in acquiring it. RetryCount = %d",
// args.retryCount);
}
});

auto result{ co_await channelOperation };

if (result.Status() == winrt::PushNotificationChannelStatus::CompletedSuccess)
{
auto channel{ result.Channel() };

//std::cout << "\nWNS Channel URI: " << winrt::to_string(channel.Uri().ToString()) << std::endl;

// It's the caller's responsibility to keep the channel alive
co_return channel;
}
else if (result.Status() == winrt::PushNotificationChannelStatus::CompletedFailure)
{
//LOG_HR_MSG(result.ExtendedError(), "We hit a critical non-retryable error with the WNS Channel URI request!");
co_return nullptr;
}
else
{
//LOG_HR_MSG(result.ExtendedError(), "Some other failure occurred.");
co_return nullptr;
}

};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once
#include <winrt/Microsoft.Windows.AppNotifications.h>
#include <winrt/Microsoft.Windows.PushNotifications.h>

struct PushToastWithAvatar
{
public:
static const unsigned ScenarioId{ 1 };
static const wchar_t* ScenarioName;

static const winrt::guid RemoteId;

static bool SendToast();
static void NotificationReceived(winrt::Microsoft::Windows::AppNotifications::AppNotificationActivatedEventArgs const& notificationActivatedEventArgs);
static winrt::Microsoft::Windows::PushNotifications::PushNotificationChannel RequestChannel();
static winrt::Windows::Foundation::IAsyncOperation<winrt::Microsoft::Windows::PushNotifications::PushNotificationChannel> RequestChannelAsync();
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ namespace CppUnpackagedAppNotifications
Scenario2_ToastWithTextBox();
}

/* The following code is template-specific IDL.
These runtime classes are the same across all C++/WinRT WinUI samples. */
[default_interface]
runtimeclass Scenario3_PushToastWithAvatar : Microsoft.UI.Xaml.Controls.Page
{
Scenario3_PushToastWithAvatar();
}

/* The following code is template-specific IDL.
These runtime classes are the same across all C++/WinRT WinUI samples. */

runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "MainPage.xaml.h"
#include "Notifications\ToastWithAvatar.h"
#include "Notifications\ToastWithTextBox.h"
#include "Notifications\PushToastWithAvatar.h"

namespace winrt
{
Expand All @@ -19,7 +20,8 @@ namespace winrt::CppUnpackagedAppNotifications
IVector<Scenario> implementation::MainPage::scenariosInner = single_threaded_observable_vector<Scenario>(
{
Scenario{ ToastWithAvatar::ScenarioName, hstring(name_of<CppUnpackagedAppNotifications::Scenario1_ToastWithAvatar>())},
Scenario{ ToastWithTextBox::ScenarioName, hstring(name_of<CppUnpackagedAppNotifications::Scenario2_ToastWithTextBox>())}
Scenario{ ToastWithTextBox::ScenarioName, hstring(name_of<CppUnpackagedAppNotifications::Scenario2_ToastWithTextBox>())},
Scenario{ PushToastWithAvatar::ScenarioName, hstring(name_of<CppUnpackagedAppNotifications::Scenario3_PushToastWithAvatar>())}
});

hstring SampleConfig::FeatureName{ L"CppUnpackagedAppNotifications" };
Expand Down
Loading