diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/App.xaml.cpp b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/App.xaml.cpp index b709860c2..77a8302ce 100644 --- a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/App.xaml.cpp +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/App.xaml.cpp @@ -9,13 +9,15 @@ #include #include #include +#include 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 @@ -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() }; + + // 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() }; g_notificationManager.ProcessLaunchActivationArgs(notificationActivatedEventArgs); } + break; + } } } diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications.vcxproj b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications.vcxproj index 58a042ac6..0b98bc472 100644 --- a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications.vcxproj +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications.vcxproj @@ -120,6 +120,7 @@ + @@ -139,6 +140,9 @@ Scenario2_ToastWithTextBox.xaml Code + + Scenario3_PushToastWithAvatar.xaml + SettingsPage.xaml Code @@ -156,6 +160,7 @@ Designer + Designer @@ -169,6 +174,7 @@ Code + @@ -191,6 +197,9 @@ Scenario2_ToastWithTextBox.xaml Code + + Scenario3_PushToastWithAvatar.xaml + SettingsPage.xaml Code diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications.vcxproj.filters b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications.vcxproj.filters index 299536593..b88b277e2 100644 --- a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications.vcxproj.filters +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications.vcxproj.filters @@ -10,6 +10,7 @@ + @@ -28,6 +29,9 @@ Notifications + + Notifications + @@ -45,6 +49,9 @@ Notifications + + Notifications + @@ -80,4 +87,7 @@ + + + \ No newline at end of file diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/NotificationManager.cpp b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/NotificationManager.cpp index eb953137a..0ee494286 100644 --- a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/NotificationManager.cpp +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/NotificationManager.cpp @@ -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; } @@ -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(); @@ -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; + }) }; +} diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/NotificationManager.h b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/NotificationManager.h index f0ea77b74..d646dc711 100644 --- a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/NotificationManager.h +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/NotificationManager.h @@ -3,6 +3,7 @@ #pragma once #include +#include class NotificationManager { @@ -15,6 +16,7 @@ class NotificationManager private: bool DispatchNotification(winrt::Microsoft::Windows::AppNotifications::AppNotificationActivatedEventArgs const& notificationActivatedEventArgs); + void SubscribeForegroundEventHandler(); bool m_isRegistered; }; diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/PushToastWithAvatar.cpp b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/PushToastWithAvatar.cpp new file mode 100644 index 000000000..0faa1d7e4 --- /dev/null +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/PushToastWithAvatar.cpp @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "PushToastWithAvatar.h" +#include "Common.h" +#include +#include +#include +#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 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; + } + +}; diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/PushToastWithAvatar.h b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/PushToastWithAvatar.h new file mode 100644 index 000000000..add74e75a --- /dev/null +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Notifications/PushToastWithAvatar.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once +#include +#include + +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 RequestChannelAsync(); +}; diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Project.idl b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Project.idl index e83ce07b8..66ab7b8e8 100644 --- a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Project.idl +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Project.idl @@ -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 { diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/SampleConfiguration.cpp b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/SampleConfiguration.cpp index d619ac12f..ca1a33c65 100644 --- a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/SampleConfiguration.cpp +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/SampleConfiguration.cpp @@ -7,6 +7,7 @@ #include "MainPage.xaml.h" #include "Notifications\ToastWithAvatar.h" #include "Notifications\ToastWithTextBox.h" +#include "Notifications\PushToastWithAvatar.h" namespace winrt { @@ -19,7 +20,8 @@ namespace winrt::CppUnpackagedAppNotifications IVector implementation::MainPage::scenariosInner = single_threaded_observable_vector( { Scenario{ ToastWithAvatar::ScenarioName, hstring(name_of())}, - Scenario{ ToastWithTextBox::ScenarioName, hstring(name_of())} + Scenario{ ToastWithTextBox::ScenarioName, hstring(name_of())}, + Scenario{ PushToastWithAvatar::ScenarioName, hstring(name_of())} }); hstring SampleConfig::FeatureName{ L"CppUnpackagedAppNotifications" }; diff --git a/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Scenario3_PushToastWithAvatar.xaml b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Scenario3_PushToastWithAvatar.xaml new file mode 100644 index 000000000..0666aa874 --- /dev/null +++ b/Samples/Notifications/App/CppUnpackagedAppNotifications/CppUnpackagedAppNotifications/Scenario3_PushToastWithAvatar.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + This example demonstrates using a raw XML payload to produce a local toast notification with an avatar image and activation. + + +