From 5e4961a2a7c79115cd128a0be66ff9094d032eaf Mon Sep 17 00:00:00 2001 From: eric langlois Date: Wed, 3 Aug 2022 21:30:46 -0700 Subject: [PATCH] AppNotificationBuilder - ProgressBar (#2780) * First iteration * Fix formatting issues * Remove all references to toast * Add ProgressBar * Remove getters and other nits * Add XML outputs for all examples * Update examples and fix missing png * Add limitations * Add ArgumentSerializer and rename APIs * Fix some nits * Fix audio ctors * Add Deserializer functions and example * Apply review changes * Update AppNotificationBuilder-spec.md * Initial commit * Working on it * Add AppNotificationContent * Current impl * Working on unit tests * Added unit tests * Add Audio tests * Update APITests.cpp * Remove spec from PR * Rename AppNotification Builder API to match specs (#2766) * Renaming to match specs * Adding latest changes to specs * Code cleanup * Updating to reflect latest changes to the specs doc Co-authored-by: Eric Langlois * Initial implementation * Update the spec with BuildNotification * Remove unused files * Add copyright to all files * Update AppNotificationBuilder.idl Adding copyright notice * Update pch.cpp Adding copyright notice * Update WindowsAppRuntime_DLL.vcxproj Removing references to fmt lib * Update packages.config Removing reference to fmt * Update AppNotificationBuilder.cpp Removing fmt since we won't be using and to unblock the build pipelines * Fixing build break * Addressing comments * Update error codes with messages * Add helper file * Add SetTimestamp impl * Add AppNotificationAudioLooping * Add IsSupported to attributes * Don't mix binding and value states * Code cleanup * Using printf as it is the norm for the builder * look proper * spacing * Reorder functions in idl to help github track changes properly * Keeping all progressBar tests together * PR feedback Co-authored-by: Paul Purifoy Co-authored-by: Paul Purifoy <33183370+pmpurifoy@users.noreply.github.com> Co-authored-by: Eric Langlois --- .../AppNotificationBuilder.cpp | 53 ++++++--- .../AppNotificationBuilder.h | 4 + .../AppNotificationBuilder.idl | 31 ++++- .../AppNotificationBuilder.vcxitems | 4 +- .../AppNotificationProgressBar.cpp | 112 ++++++++++++++++++ .../AppNotificationProgressBar.h | 61 ++++++++++ test/AppNotificationBuilderTests/APITests.cpp | 87 ++++++++++++++ .../appxmanifest.xml | 1 + 8 files changed, 334 insertions(+), 19 deletions(-) create mode 100644 dev/AppNotifications/AppNotificationBuilder/AppNotificationProgressBar.cpp create mode 100644 dev/AppNotifications/AppNotificationBuilder/AppNotificationProgressBar.h diff --git a/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.cpp b/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.cpp index ac191b4a23..1982306d7e 100644 --- a/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.cpp +++ b/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.cpp @@ -200,6 +200,13 @@ namespace winrt::Microsoft::Windows::AppNotifications::Builder::implementation return *this; } + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationBuilder AppNotificationBuilder::AddProgressBar(AppNotificationProgressBar const& value) + { + m_progressBarList.push_back(value); + + return *this; + } + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationBuilder AppNotificationBuilder::AddTextBox(hstring id) { ThrowIfMaxInputItemsExceeded(); @@ -347,28 +354,40 @@ namespace winrt::Microsoft::Windows::AppNotifications::Builder::implementation return m_useButtonStyle ? L" useButtonStyle='true'" : L""; } - winrt::Microsoft::Windows::AppNotifications::AppNotification AppNotificationBuilder::BuildNotification() + std::wstring AppNotificationBuilder::GetProgressBars() { - std::wstring xmlResult{}; - xmlResult.reserve(c_maxAppNotificationPayload); + std::wstring result{}; + for (auto progressBar : m_progressBarList) + { + result.append(progressBar.as().ToString()); + } + + return result; + } + winrt::Microsoft::Windows::AppNotifications::AppNotification AppNotificationBuilder::BuildNotification() + { // Build the button string and fill m_useButtonStyle std::wstring actions{ GetActions() }; - xmlResult.append(L""); - xmlResult.append(GetText()); - xmlResult.append(m_attributionText); - xmlResult.append(GetImages()); - xmlResult.append(L""); - xmlResult.append(m_audio.c_str()); - xmlResult.append(actions); - xmlResult.append(L""); + auto xmlResult{ wil::str_printf(L"%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls", + L"", + GetText().c_str(), + m_attributionText.c_str(), + GetImages().c_str(), + GetProgressBars().c_str(), + L"", + m_audio.c_str(), + actions.c_str(), + L"") }; + + THROW_HR_IF_MSG(E_INVALIDARG, xmlResult.size() > c_maxAppNotificationPayload, "Maximum payload size exceeded"); winrt::Microsoft::Windows::AppNotifications::AppNotification appNotification{ xmlResult }; appNotification.Tag(m_tag); diff --git a/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.h b/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.h index 473ce20028..8433aceac6 100644 --- a/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.h +++ b/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.h @@ -54,6 +54,8 @@ namespace winrt::Microsoft::Windows::AppNotifications::Builder::implementation // Adds a button to the AppNotificationBuilder winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationBuilder AddButton(AppNotificationButton const& value); + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationBuilder AddProgressBar(AppNotificationProgressBar const& value); + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationBuilder AddComboBox(AppNotificationComboBox const& value); winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationBuilder SetTag(winrt::hstring const& value); @@ -72,6 +74,7 @@ namespace winrt::Microsoft::Windows::AppNotifications::Builder::implementation std::wstring GetText(); std::wstring GetImages(); std::wstring GetActions(); + std::wstring GetProgressBars(); std::wstring m_timeStamp{}; AppNotificationDuration m_duration{ AppNotificationDuration::Default }; @@ -85,6 +88,7 @@ namespace winrt::Microsoft::Windows::AppNotifications::Builder::implementation winrt::hstring m_audio{}; winrt::Windows::Foundation::Collections::IMap m_arguments{ winrt::single_threaded_map() }; std::vector m_buttonList{}; + std::vector m_progressBarList{}; std::vector m_textBoxList{}; std::vector m_comboBoxList{}; winrt::hstring m_tag{}; diff --git a/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.idl b/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.idl index b9981490a6..a8c7ff9c9c 100644 --- a/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.idl +++ b/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.idl @@ -17,7 +17,7 @@ namespace Microsoft.Windows.AppNotifications.Builder AppNotificationTextProperties SetLanguage(String value); AppNotificationTextProperties SetIncomingCallAlignment(); AppNotificationTextProperties SetMaxLines(Int32 value); - } + }; enum AppNotificationButtonStyle { @@ -65,6 +65,33 @@ namespace Microsoft.Windows.AppNotifications.Builder AppNotificationButton SetInvokeUri(Windows.Foundation.Uri protocolUri, String targetAppId); }; + runtimeclass AppNotificationProgressBar + { + // AppNotificationProgressBar binds to AppNotificationProgressData so the AppNotification will + // receive every update to the status and value. In the WinAppSDK, these binding + // values are static, so developers won't need to define these binding values + // themselves. + AppNotificationProgressBar(); + + // Setting these properties will remove the data binding with a static value + String Title; + String Status; + Double Value; + String ValueStringOverride; + + AppNotificationProgressBar SetTitle(String value); + AppNotificationProgressBar BindTitle(); + + AppNotificationProgressBar SetStatus(String value); + AppNotificationProgressBar BindStatus(); + + AppNotificationProgressBar SetValue(Double value); + AppNotificationProgressBar BindValue(); + + AppNotificationProgressBar SetValueStringOverride(String value); + AppNotificationProgressBar BindValueStringOverride(); + }; + runtimeclass AppNotificationComboBox { AppNotificationComboBox(String id); @@ -193,6 +220,8 @@ namespace Microsoft.Windows.AppNotifications.Builder AppNotificationBuilder AddComboBox(AppNotificationComboBox value); + AppNotificationBuilder AddProgressBar(AppNotificationProgressBar value); + // Constructs a WindowsAppSDK AppNotification object with the XML payload Microsoft.Windows.AppNotifications.AppNotification BuildNotification(); diff --git a/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.vcxitems b/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.vcxitems index 34f4866425..84cd82cdd0 100644 --- a/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.vcxitems +++ b/dev/AppNotifications/AppNotificationBuilder/AppNotificationBuilder.vcxitems @@ -19,6 +19,7 @@ + @@ -26,7 +27,8 @@ + - \ No newline at end of file + diff --git a/dev/AppNotifications/AppNotificationBuilder/AppNotificationProgressBar.cpp b/dev/AppNotifications/AppNotificationBuilder/AppNotificationProgressBar.cpp new file mode 100644 index 0000000000..f2380a2cf6 --- /dev/null +++ b/dev/AppNotifications/AppNotificationBuilder/AppNotificationProgressBar.cpp @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#include "pch.h" +#include "AppNotificationProgressBar.h" +#include "Microsoft.Windows.AppNotifications.Builder.AppNotificationProgressBar.g.cpp" + +namespace winrt::Microsoft::Windows::AppNotifications::Builder::implementation +{ + AppNotificationProgressBar::AppNotificationProgressBar() + :m_titleBindMode{ BindMode::NotSet }, + m_statusBindMode{ BindMode::NotSet }, + m_valueBindMode{ BindMode::NotSet }, + m_valueStringOverrideBindMode{ BindMode::NotSet } + {}; + + void AppNotificationProgressBar::Title(winrt::hstring const& value) + { + m_title = value; + m_titleBindMode = BindMode::Value; + } + + void AppNotificationProgressBar::Status(winrt::hstring const& value) + { + m_status = value; + m_statusBindMode = BindMode::Value; + } + + void AppNotificationProgressBar::Value(double value) + { + THROW_HR_IF_MSG(E_INVALIDARG, value < 0.0 || value > 1.0, "You must provide a value between 0.0 and 1.0"); + + m_value = value; + m_valueBindMode = BindMode::Value; + } + + void AppNotificationProgressBar::ValueStringOverride(winrt::hstring const& value) + { + m_valueStringOverride = value; + m_valueStringOverrideBindMode = BindMode::Value; + } + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar AppNotificationProgressBar::SetTitle(winrt::hstring const& value) + { + Title(value); + + return *this; + } + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar AppNotificationProgressBar::BindTitle() + { + m_titleBindMode = BindMode::Bind; + + return *this; + } + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar AppNotificationProgressBar::SetStatus(winrt::hstring const& value) + { + Status(value); + + return *this; + } + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar AppNotificationProgressBar::BindStatus() + { + m_statusBindMode = BindMode::Bind; + + return *this; + } + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar AppNotificationProgressBar::SetValue(double value) + { + Value(value); + + return *this; + } + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar AppNotificationProgressBar::BindValue() + { + m_valueBindMode = BindMode::Bind; + + return *this; + } + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar AppNotificationProgressBar::SetValueStringOverride(winrt::hstring const& value) + { + ValueStringOverride(value); + + return *this; + } + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar AppNotificationProgressBar::BindValueStringOverride() + { + m_valueStringOverrideBindMode = BindMode::Bind; + + return *this; + } + + winrt::hstring AppNotificationProgressBar::ToString() + { + auto title{ wil::str_printf(L" title='%ls'", m_titleBindMode == BindMode::Value ? m_title.c_str() : L"{progressTitle}") }; + auto status{ wil::str_printf(L" status='%ls'", m_statusBindMode == BindMode::Value ? m_status.c_str() : L"{progressStatus}") }; + auto value{ wil::str_printf(L" value='%ls'", m_valueBindMode == BindMode::Value ? wil::str_printf(L"%g", m_value).c_str() : L"{progressValue}") }; + auto valueStringOverride{ wil::str_printf < std::wstring>(L" valueStringOverride='%ls'", m_valueStringOverrideBindMode == BindMode::Value ? m_valueStringOverride.c_str() : L"{progressValueString}") }; + + return wil::str_printf(L"", + m_titleBindMode == BindMode::NotSet ? L"" :title.c_str(), + status.c_str(), + value.c_str(), + m_valueStringOverrideBindMode == BindMode::NotSet ? L"" : valueStringOverride.c_str()).c_str(); + } +} diff --git a/dev/AppNotifications/AppNotificationBuilder/AppNotificationProgressBar.h b/dev/AppNotifications/AppNotificationBuilder/AppNotificationProgressBar.h new file mode 100644 index 0000000000..49b5edf296 --- /dev/null +++ b/dev/AppNotifications/AppNotificationBuilder/AppNotificationProgressBar.h @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#pragma once +#include "Microsoft.Windows.AppNotifications.Builder.AppNotificationProgressBar.g.h" + +namespace winrt::Microsoft::Windows::AppNotifications::Builder::implementation +{ + + struct AppNotificationProgressBar : AppNotificationProgressBarT + { + AppNotificationProgressBar(); + + // Properties + void Title(winrt::hstring const& value); + winrt::hstring Title() { return m_title; }; + + void Status(winrt::hstring const& value); + winrt::hstring Status() { return m_status; }; + + void Value(double value); + double Value() { return m_value; }; + + void ValueStringOverride(winrt::hstring const& value); + winrt::hstring ValueStringOverride() { return m_valueStringOverride; }; + + // Fluent setters + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar SetTitle(winrt::hstring const& value); + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar BindTitle(); + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar SetStatus(winrt::hstring const& value); + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar BindStatus(); + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar SetValue(double value); + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar BindValue(); + + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar SetValueStringOverride(winrt::hstring const& value); + winrt::Microsoft::Windows::AppNotifications::Builder::AppNotificationProgressBar BindValueStringOverride(); + + // IStringable + winrt::hstring ToString(); + + private: + enum class BindMode {NotSet, Bind, Value}; + + BindMode m_titleBindMode; + winrt::hstring m_title; + BindMode m_statusBindMode; + winrt::hstring m_status; + BindMode m_valueBindMode; + double m_value; + BindMode m_valueStringOverrideBindMode; + winrt::hstring m_valueStringOverride; + }; +} +namespace winrt::Microsoft::Windows::AppNotifications::Builder::factory_implementation +{ + struct AppNotificationProgressBar : AppNotificationProgressBarT + { + }; +} diff --git a/test/AppNotificationBuilderTests/APITests.cpp b/test/AppNotificationBuilderTests/APITests.cpp index ef4d760800..e1d64bc29d 100644 --- a/test/AppNotificationBuilderTests/APITests.cpp +++ b/test/AppNotificationBuilderTests/APITests.cpp @@ -382,6 +382,93 @@ namespace Test::AppNotification::Builder VERIFY_ARE_EQUAL(builder.BuildNotification().Payload(), expected); } + TEST_METHOD(AppNotificationBuilderBuildNotificationWithTooLargePayload) + { + VERIFY_THROWS_HR(AppNotificationBuilder() + .AddText(std::wstring(5120, 'A').c_str()) + .BuildNotification(), E_INVALIDARG); + } + + TEST_METHOD(AppNotificationAddProgressBar) + { + auto builder{ AppNotificationBuilder() + .AddText(L"Downloading this week's new music...") + .AddProgressBar(AppNotificationProgressBar() + .BindTitle() + .BindValueStringOverride()) }; + auto expected{ L"Downloading this week's new music..." }; + + VERIFY_ARE_EQUAL(builder.BuildNotification().Payload(), expected); + } + + TEST_METHOD(AppNotificationAddMoreThanOneProgressBar) + { + auto builder{ AppNotificationBuilder() + .AddText(L"Downloading this week's new music...") + .AddProgressBar(AppNotificationProgressBar() + .BindTitle() + .BindValueStringOverride()) + .AddProgressBar(AppNotificationProgressBar() + .SetValue(0.8) + .SetStatus(L"Still downloading...")) }; + auto expected{ L"Downloading this week's new music..." }; + + VERIFY_ARE_EQUAL(builder.BuildNotification().Payload(), expected); + } + + TEST_METHOD(AppNotificationProgressBarDefaults) + { + auto progressBar{ AppNotificationProgressBar() }; + auto expected{ L"" }; + + VERIFY_ARE_EQUAL(progressBar.as().ToString(), expected); + } + + TEST_METHOD(AppNotificationProgressBarSetSpecificValue) + { + auto progressBar{ AppNotificationProgressBar() }; + progressBar.Value(0.8); + auto expected{ L"" }; + + VERIFY_ARE_EQUAL(progressBar.as().ToString(), expected); + } + + TEST_METHOD(AppNotificationProgressBarSetValueLargerThanOne) + { + auto progressBar{ AppNotificationProgressBar() }; + VERIFY_THROWS_HR(progressBar.Value(1.01), E_INVALIDARG); + + VERIFY_THROWS_HR(AppNotificationProgressBar().SetValue(1.01), E_INVALIDARG); + } + + TEST_METHOD(AppNotificationProgressBarSetValueSmallerThanZero) + { + auto progressBar{ AppNotificationProgressBar() }; + VERIFY_THROWS_HR(progressBar.Value(-0.1), E_INVALIDARG); + + VERIFY_THROWS_HR(AppNotificationProgressBar().SetValue(-0.1), E_INVALIDARG); + } + + TEST_METHOD(AppNotificationProgressBarSetSpecificValueThenChangeToBind) + { + auto progressBar{ AppNotificationProgressBar() }; + progressBar.Value(0.8); + progressBar.BindValue(); + auto expected{ L"" }; + + VERIFY_ARE_EQUAL(progressBar.as().ToString(), expected); + } + + TEST_METHOD(AppNotificationProgressBarBindTitleThenChangeToSpecificText) + { + auto progressBar{ AppNotificationProgressBar() + .BindTitle() + .SetTitle(L"Specific title") }; + auto expected{ L"" }; + + VERIFY_ARE_EQUAL(progressBar.as().ToString(), expected); + } + TEST_METHOD(AppNotificationBuilderAddTextBox) { auto builder{ AppNotificationBuilder() diff --git a/test/DynamicDependency/data/Microsoft.WindowsAppRuntime.Framework/appxmanifest.xml b/test/DynamicDependency/data/Microsoft.WindowsAppRuntime.Framework/appxmanifest.xml index 7c1dfb7ae5..70a43a9ca6 100644 --- a/test/DynamicDependency/data/Microsoft.WindowsAppRuntime.Framework/appxmanifest.xml +++ b/test/DynamicDependency/data/Microsoft.WindowsAppRuntime.Framework/appxmanifest.xml @@ -87,6 +87,7 @@ +