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

Introduce Register Overload for assets #2596

Merged
merged 6 commits into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 2 additions & 2 deletions dev/AppNotifications/AppNotificationManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ namespace winrt::Microsoft::Windows::AppNotifications::implementation
{
THROW_HR_IF_MSG(E_ILLEGAL_METHOD_CALL, AppModel::Identity::IsPackagedProcess(), "Not applicable for packaged applications");

THROW_HR_IF(E_INVALIDARG, (displayName == winrt::hstring{}) || (iconUri == nullptr));
THROW_HR_IF(E_INVALIDARG, displayName.empty() || (iconUri == nullptr));

AppNotificationAssets assets{ GetAssets(displayName.c_str(), iconUri.RawUri().c_str()) };
AppNotificationAssets assets{ ValidateAssets(displayName, iconUri.RawUri().c_str()) };

{
auto lock{ m_lock.lock_exclusive() };
Expand Down
6 changes: 3 additions & 3 deletions dev/AppNotifications/AppNotificationUtility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,13 @@ AppNotificationAssets Microsoft::Windows::AppNotifications::Helpers::GetAssets()
return assets;
}

AppNotificationAssets Microsoft::Windows::AppNotifications::Helpers::GetAssets(std::wstring displayName,std::wstring iconFilePath)
AppNotificationAssets Microsoft::Windows::AppNotifications::Helpers::ValidateAssets(winrt::hstring const& displayName, std::filesystem::path const& iconFilePath)
{
winrt::check_bool(std::filesystem::exists(iconFilePath));
sharath2727 marked this conversation as resolved.
Show resolved Hide resolved

THROW_HR_IF_MSG(E_INVALIDARG, !IsIconFileExtensionSupported(std::filesystem::path{ iconFilePath }), "Icon format not supported");
THROW_HR_IF_MSG(E_INVALIDARG, !IsIconFileExtensionSupported(iconFilePath), "Icon format not supported");

return AppNotificationAssets{ std::move(displayName), std::move(iconFilePath) };
return AppNotificationAssets{ displayName.c_str(), iconFilePath.wstring() };
}

void Microsoft::Windows::AppNotifications::Helpers::RegisterAssets(std::wstring const& appId, std::wstring const& clsid, AppNotificationAssets const& assets)
Expand Down
2 changes: 1 addition & 1 deletion dev/AppNotifications/AppNotificationUtility.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,5 @@ namespace Microsoft::Windows::AppNotifications::Helpers

Microsoft::Windows::AppNotifications::ShellLocalization::AppNotificationAssets GetAssets();

Microsoft::Windows::AppNotifications::ShellLocalization::AppNotificationAssets GetAssets(std::wstring displayName, std::wstring iconFilePath);
Microsoft::Windows::AppNotifications::ShellLocalization::AppNotificationAssets ValidateAssets(winrt::hstring const& displayName, std::filesystem::path const& iconFilePath);
}
5 changes: 3 additions & 2 deletions dev/AppNotifications/AppNotifications.idl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "..\AppLifecycle\AppLifecycle.idl";

namespace Microsoft.Windows.AppNotifications
{
[contractversion(1)]
[contractversion(2)]
apicontract AppNotificationsContract {}

// Event args for the Notification Activation
Expand Down Expand Up @@ -123,7 +123,8 @@ namespace Microsoft.Windows.AppNotifications
// For Unpackaged apps, the caller process will be registered as the COM server. And assets like displayname and icon will be gleaned from Shell and registered as well.
void Register();

// For Unpackaged apps only, the caller process will be registered as the COM server.
// Unpackaged Apps can call this API to register custom displayname and icon for AppNotifications and register themselves as a COM server.
[contract(AppNotificationsContract, 2)]
void Register(String displayName, Windows.Foundation.Uri iconUri);

// Unregisters the COM Service so that a subsequent activation will launch a new process
Expand Down
2 changes: 1 addition & 1 deletion dev/AppNotifications/ShellLocalization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ HRESULT Microsoft::Windows::AppNotifications::ShellLocalization::DeleteIconFromC
std::path iconFilePath{ RetrieveLocalFolderPath() / (notificationAppId + c_pngExtension) };

// If DeleteFile returned FALSE, then deletion failed and we should return the corresponding error code.
winrt::check_bool(DeleteFileW(iconFilePath.c_str()) != FALSE);
RETURN_IF_WIN32_BOOL_FALSE(DeleteFileW(iconFilePath.c_str()));
sharath2727 marked this conversation as resolved.
Show resolved Hide resolved

return S_OK;
}
Expand Down
28 changes: 28 additions & 0 deletions specs/AppNotifications/AppNotifications-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,31 @@ int main()
}
```

## Registering for App Notifications using assets

For Unpackaged applications, the developer can Register using a custom Display Name and Icon.
WinAppSDK will register the application and display these assets when an App Notification is received.
The developer should provide both the assets or not provide them at all. Icon provided by the developer
should be a valid supported format. The API supports the formats - png, bmp, jpg. The icon
should reside on the local machine only otherwise the API throws an exception. For Packaged applications,
this API is not applicable and will throw an exception.

```cpp
int main()
{
auto manager = winrt::AppNotificationManager::Default();

std::wstring iconFilepath{ std::filesystem::current_path() / "icon.ico" };
manager.Register(L"AppNotifications", winrt::Windows::Foundation::Uri {iconFilepath});
sharath2727 marked this conversation as resolved.
Show resolved Hide resolved

// other app init and then message loop here

// Call Unregister() before exiting main so that subsequent invocations will launch a new process
manager.Unregister();
return 0;
}
```

## Displaying an App Notification

To display a Notification, an app needs to define a payload in xml. In the example below, the
Expand Down Expand Up @@ -451,6 +476,9 @@ namespace Microsoft.Windows.AppNotifications
// For Unpackaged apps, the caller process will be registered as the COM server. And assets like displayname and icon will be gleaned from Shell and registered as well.
void Register();

// For Unpackaged apps only, the caller process will be registered as the COM server.
sharath2727 marked this conversation as resolved.
Show resolved Hide resolved
void Register(String displayName, Windows.Foundation.Uri iconUri);

// Unregisters the COM Service so that a subsequent activation will launch a new process
void Unregister();

Expand Down
66 changes: 8 additions & 58 deletions test/TestApps/ToastNotificationsTestApp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace winrt
const std::wstring c_localWindowsAppSDKFolder{ LR"(\Microsoft\WindowsAppSDK\)" };
const std::wstring c_pngExtension{ LR"(.png)" };
const std::wstring c_appUserModelId{ LR"(TaefTestAppId)" };
const std::wstring c_iconFilepath{ std::filesystem::current_path() / "icon1.ico" };

bool BackgroundActivationTest() // Activating application for background test.
{
Expand Down Expand Up @@ -1328,10 +1329,7 @@ bool VerifyRegisterWithNullDisplayNameFail_Unpackaged()
winrt::AppNotificationManager::Default().UnregisterAll();
try
{
winrt::AppNotificationManager::Default().Register(winrt::hstring{}, winrt::Uri{ LR"(C:\Windows\System32\WindowsSecurityIcon.png)" });


winrt::AppNotificationManager::Default().UnregisterAll();
winrt::AppNotificationManager::Default().Register(winrt::hstring{}, winrt::Uri{ c_iconFilepath });
}
catch (...)
{
Expand All @@ -1348,8 +1346,6 @@ bool VerifyRegisterWithNullIconFail_Unpackaged()
try
{
winrt::AppNotificationManager::Default().Register(L"AppNotificationApp", nullptr);

winrt::AppNotificationManager::Default().UnregisterAll();
}
catch (...)
{
Expand All @@ -1366,9 +1362,6 @@ bool VerifyRegisterWithNullDisplayNameAndNullIconFail_Unpackaged()
try
{
winrt::AppNotificationManager::Default().Register(winrt::hstring{}, nullptr);


winrt::AppNotificationManager::Default().UnregisterAll();
}
catch (...)
{
Expand All @@ -1378,15 +1371,15 @@ bool VerifyRegisterWithNullDisplayNameAndNullIconFail_Unpackaged()
return true;
}

bool VerifyRegisterWithDisplayNameAndIcon_Unpackaged()
bool VerifyShowToastWithCustomDisplayNameAndIcon_Unpackaged()
{
// Register is already called in main with an explicit appusermodelId
winrt::AppNotificationManager::Default().UnregisterAll();
try
{
winrt::AppNotificationManager::Default().Register(L"AppNotificationApp", winrt::Uri{ LR"(C:\Windows\System32\WindowsSecurityIcon.png)" });
winrt::AppNotificationManager::Default().Register(L"AppNotificationApp", winrt::Uri{ c_iconFilepath });

winrt::AppNotificationManager::Default().UnregisterAll();
winrt::check_bool(VerifyShowToast_Unpackaged());
}
catch (...)
{
Expand All @@ -1403,8 +1396,6 @@ bool VerifyRegisterWithDisplayNameAndInvalidIconPathFail_Unpackaged()
try
{
winrt::AppNotificationManager::Default().Register(L"AppNotificationApp", winrt::Uri{ LR"(C:\InvalidPath\)" });

winrt::AppNotificationManager::Default().UnregisterAll();
}
catch (...)
{
Expand All @@ -1421,9 +1412,7 @@ bool VerifyRegisterWithEmptyDisplayNameFail_Unpackaged()
try
{
// hstring treats L"" as assigning nullptr
winrt::AppNotificationManager::Default().Register(L"", winrt::Uri{ LR"(C:\Windows\System32\WindowsSecurityIcon.png)" });

winrt::AppNotificationManager::Default().UnregisterAll();
winrt::AppNotificationManager::Default().Register(L"", winrt::Uri{ c_iconFilepath });
}
catch (...)
{
Expand All @@ -1433,51 +1422,14 @@ bool VerifyRegisterWithEmptyDisplayNameFail_Unpackaged()
return false;
}

bool VerifyRegisterWithEmptyIconFail_Unpackaged()
{
// Register is already called in main with an explicit appusermodelId
winrt::AppNotificationManager::Default().UnregisterAll();
try
{
winrt::AppNotificationManager::Default().Register(L"AppNotificationApp", winrt::Uri{ L"" });

winrt::AppNotificationManager::Default().UnregisterAll();
}
catch (...)
{
return winrt::to_hresult() == E_POINTER;
}

return false;
}

bool VerifyRegisterWithEmptyDisplayNameAndEmptyIconFail_Unpackaged()
{
// Register is already called in main with an explicit appusermodelId
winrt::AppNotificationManager::Default().UnregisterAll();
try
{
winrt::AppNotificationManager::Default().Register(L"", winrt::Uri{ L"" });

winrt::AppNotificationManager::Default().UnregisterAll();
}
catch (...)
{
return winrt::to_hresult() == E_POINTER;
}

return false;
}

bool VerifyRegisterWithAssetsFail()
sharath2727 marked this conversation as resolved.
Show resolved Hide resolved
{
// Register is already called in main with an explicit appusermodelId
winrt::AppNotificationManager::Default().UnregisterAll();
try
{
// API fails for Packaged Scenario
winrt::AppNotificationManager::Default().Register(L"AppNotificationApp", winrt::Uri{ LR"(C:\InvalidPath\)" });
sharath2727 marked this conversation as resolved.
Show resolved Hide resolved

winrt::AppNotificationManager::Default().UnregisterAll();
}
catch (...)
{
Expand Down Expand Up @@ -1549,11 +1501,9 @@ std::map<std::string, bool(*)()> const& GetSwitchMapping()
{ "VerifyRegisterWithNullDisplayNameFail_Unpackaged", &VerifyRegisterWithNullDisplayNameFail_Unpackaged},
{ "VerifyRegisterWithNullIconFail_Unpackaged", &VerifyRegisterWithNullIconFail_Unpackaged},
{ "VerifyRegisterWithNullDisplayNameAndNullIconFail_Unpackaged", &VerifyRegisterWithNullDisplayNameAndNullIconFail_Unpackaged},
{ "VerifyRegisterWithDisplayNameAndIcon_Unpackaged", &VerifyRegisterWithDisplayNameAndIcon_Unpackaged},
{ "VerifyShowToastWithCustomDisplayNameAndIcon_Unpackaged", &VerifyShowToastWithCustomDisplayNameAndIcon_Unpackaged},
{ "VerifyRegisterWithDisplayNameAndInvalidIconPathFail_Unpackaged", &VerifyRegisterWithDisplayNameAndInvalidIconPathFail_Unpackaged},
{ "VerifyRegisterWithEmptyDisplayNameFail_Unpackaged", &VerifyRegisterWithEmptyDisplayNameFail_Unpackaged},
{ "VerifyRegisterWithEmptyIconFail_Unpackaged", &VerifyRegisterWithEmptyIconFail_Unpackaged},
{ "VerifyRegisterWithEmptyDisplayNameAndEmptyIconFail_Unpackaged", &VerifyRegisterWithEmptyDisplayNameAndEmptyIconFail_Unpackaged},
{ "VerifyRegisterWithAssetsFail", &VerifyRegisterWithAssetsFail},
};

Expand Down
14 changes: 2 additions & 12 deletions test/ToastNotificationTests/APITests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,9 +599,9 @@ namespace Test::ToastNotifications
RunTestUnpackaged(L"VerifyRegisterWithNullDisplayNameAndNullIconFail_Unpackaged", testWaitTime());
}

TEST_METHOD(VerifyRegisterWithDisplayNameAndIcon_Unpackaged)
TEST_METHOD(VerifyShowToastWithCustomDisplayNameAndIcon_Unpackaged)
{
RunTestUnpackaged(L"VerifyRegisterWithDisplayNameAndIcon_Unpackaged", testWaitTime());
RunTestUnpackaged(L"VerifyShowToastWithCustomDisplayNameAndIcon_Unpackaged", testWaitTime());
}

TEST_METHOD(VerifyRegisterWithDisplayNameAndInvalidIconPathFail_Unpackaged)
Expand All @@ -614,16 +614,6 @@ namespace Test::ToastNotifications
RunTestUnpackaged(L"VerifyRegisterWithEmptyDisplayNameFail_Unpackaged", testWaitTime());
}

TEST_METHOD(VerifyRegisterWithEmptyIconFail_Unpackaged)
{
RunTestUnpackaged(L"VerifyRegisterWithEmptyIconFail_Unpackaged", testWaitTime());
}

TEST_METHOD(VerifyRegisterWithEmptyDisplayNameAndEmptyIconFail_Unpackaged)
{
RunTestUnpackaged(L"VerifyRegisterWithEmptyDisplayNameAndEmptyIconFail_Unpackaged", testWaitTime());
}

TEST_METHOD(VerifyRegisterWithAssetsFail)
{
RunTest(L"VerifyRegisterWithAssetsFail", testWaitTime());
Expand Down