Skip to content

Commit

Permalink
FI develop into Content Builder feature branch (#2717)
Browse files Browse the repository at this point in the history
* update newtonsoft.json (#2688)

* Introduce Register Overload for assets (#2596)

* Introduce Register Overload for assets

* Address comments

* Add contractversion to register APi

* Address comments

* Add more examples of register API usage

* Process doc for develop branch (#2383)

* Add 1.1.2 tag to bug report

Co-authored-by: Hui Chen <[email protected]>
Co-authored-by: reunion-maestro-bot <[email protected]>
Co-authored-by: Sharath Manchala <[email protected]>
Co-authored-by: MikeHillberg <[email protected]>
Co-authored-by: Gabby Bilka <[email protected]>
Co-authored-by: Gabby Bilka <[email protected]>
Co-authored-by: Eric Langlois <[email protected]>
  • Loading branch information
8 people authored Jul 12, 2022
1 parent 4355c4e commit 6149efb
Show file tree
Hide file tree
Showing 14 changed files with 323 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug-report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ body:
label: NuGet package version
description: Specify the version of Windows App SDK (or Project Reunion) you're using.
options:
- "1.1.1"
- "1.1.2"
- "1.1.0-preview3"
- "1.0.4"
- "1.0.0-preview3"
Expand Down
49 changes: 49 additions & 0 deletions dev/AppNotifications/AppNotificationManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <winrt/Windows.Foundation.Collections.h>
#include <WindowsAppRuntime.SelfContained.h>
#include <ShellLocalization.h>
#include <filesystem>

using namespace std::literals;

Expand Down Expand Up @@ -150,6 +151,54 @@ namespace winrt::Microsoft::Windows::AppNotifications::implementation
}
}

void AppNotificationManager::Register(hstring const& displayName, winrt::Windows::Foundation::Uri const& iconUri)
{
if (!IsSupported())
{
return;
}

HRESULT hr{ S_OK };

auto logTelemetry{ wil::scope_exit([&]() {
AppNotificationTelemetry::LogRegister(hr, m_appId);
}) };

try
{
THROW_HR_IF_MSG(E_ILLEGAL_METHOD_CALL, AppModel::Identity::IsPackagedProcess(), "Not applicable for packaged applications");

THROW_HR_IF(E_INVALIDARG, displayName.empty() || (iconUri == nullptr));

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

{
auto lock{ m_lock.lock_exclusive() };
THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_OPERATION_IN_PROGRESS), m_registering, "Registration is in progress!");
m_registering = true;
}

auto registeringScopeExit{ wil::scope_exit([&]()
{
auto lock { m_lock.lock_exclusive() };
m_registering = false;
}) };

winrt::guid registeredClsid{ RegisterUnpackagedApp(assets) };

// Create event handle before COM Registration otherwise if a notification arrives will lead to race condition
m_waitHandleForArgs.create();

// Register the AppNotificationManager as a COM server for Shell to Activate and Invoke
RegisterComServer(registeredClsid);
}
catch (...)
{
hr = wil::ResultFromCaughtException();
throw;
}
}

void AppNotificationManager::RegisterComServer(winrt::guid const& registeredClsid)
{
auto lock{ m_lock.lock_exclusive() };
Expand Down
1 change: 1 addition & 0 deletions dev/AppNotifications/AppNotificationManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace winrt::Microsoft::Windows::AppNotifications::implementation
static winrt::Microsoft::Windows::AppNotifications::AppNotificationManager Default();
static winrt::Windows::Foundation::IInspectable AppNotificationDeserialize(winrt::Windows::Foundation::Uri const& uri);
void Register();
void Register(hstring const& displayName, winrt::Windows::Foundation::Uri const& iconUri);
void Unregister();
void UnregisterAll();
static bool IsSupported();
Expand Down
9 changes: 9 additions & 0 deletions dev/AppNotifications/AppNotificationUtility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,15 @@ AppNotificationAssets Microsoft::Windows::AppNotifications::Helpers::GetAssets()
return assets;
}

AppNotificationAssets Microsoft::Windows::AppNotifications::Helpers::ValidateAssets(winrt::hstring const& displayName, std::filesystem::path const& iconFilePath)
{
winrt::check_bool(std::filesystem::exists(iconFilePath));

THROW_HR_IF_MSG(E_INVALIDARG, !IsIconFileExtensionSupported(iconFilePath), "Icon format not supported");

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

void Microsoft::Windows::AppNotifications::Helpers::RegisterAssets(std::wstring const& appId, std::wstring const& clsid, AppNotificationAssets const& assets)
{
wil::unique_hkey hKey;
Expand Down
2 changes: 2 additions & 0 deletions dev/AppNotifications/AppNotificationUtility.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,6 @@ namespace Microsoft::Windows::AppNotifications::Helpers
std::wstring GetDisplayNameBasedOnProcessName();

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

Microsoft::Windows::AppNotifications::ShellLocalization::AppNotificationAssets ValidateAssets(winrt::hstring const& displayName, std::filesystem::path const& iconFilePath);
}
6 changes: 5 additions & 1 deletion 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,6 +123,10 @@ 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();

// 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
void Unregister();

Expand Down
8 changes: 3 additions & 5 deletions dev/AppNotifications/ShellLocalization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ void WriteHIconToPngFile(wil::unique_hicon const& hIcon, _In_ PCWSTR pszFileName
THROW_IF_FAILED(spStreamOut->Commit(STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE));
}

bool IsIconFileExtensionSupported(std::filesystem::path const& iconFilePath)

bool Microsoft::Windows::AppNotifications::ShellLocalization::IsIconFileExtensionSupported(std::filesystem::path const& iconFilePath)
{
static PCWSTR c_supportedExtensions[]{ L".bmp", L".ico", L".jpg", L".png" };

Expand Down Expand Up @@ -258,10 +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.
if (DeleteFile(iconFilePath.c_str()) == FALSE)
{
THROW_HR(HRESULT_FROM_WIN32(GetLastError()));
}
RETURN_IF_WIN32_BOOL_FALSE(DeleteFileW(iconFilePath.c_str()));

return S_OK;
}
Expand Down
2 changes: 2 additions & 0 deletions dev/AppNotifications/ShellLocalization.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ namespace Microsoft::Windows::AppNotifications::ShellLocalization
HRESULT RetrieveAssetsFromShortcut(_Out_ Microsoft::Windows::AppNotifications::ShellLocalization::AppNotificationAssets& assets) noexcept;

HRESULT DeleteIconFromCache() noexcept;

bool IsIconFileExtensionSupported(std::filesystem::path const& iconFilePath);
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@
<PackageReference Include="MSTest.TestFramework">
<Version>1.4.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="MrtCoreManagedTests.runsettings">
Expand Down Expand Up @@ -194,4 +197,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
37 changes: 37 additions & 0 deletions docs/develop-branch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
`develop` Branch
===

Building the full Windows App SDK package involves multiple repos and build pipelines.
Beyond the publicly-visible repos, we also have closed-source repos that produce public packages which
are then consumed by this repo ([WindowsAppSDK](https://github.com/microsoft/WindowsAppSDK)).

Some of those packages can't yet be pushed to the public feed on a CI/nightly basis though.
Until that's resolved, we could get this repo into a state of being buildable only internally,
by restoring the latest package from an internal feed.

To allow either configuration to be built (using the public dependent package or the internal one),
we maintain two branches (similar to the GitFlow process):

* `main` branch consumes the latest public package
* `develop` branch consumes the latest internal package.

The `develop` branch code is in the repo and still open, but can only be built internally.
The `main` branch is always buildable at HEAD.
External contributions to the repo are made to the `main` branch,
and are merged internally into `develop` when completed.

Whenever a new public version of the dependent packages are pushed to the public feed,
the `develop` branch can be merged into `main`.
For example as part of (pre)releases.

Merging between `develop` and `main` is normal (not squashed),
so that both branches can show the correct history, just with a few extra merge commits.
Once the packages can be kept immediately updated we can drop the `develop` branch
and not lose any history.

The long-lasting difference between `main` and `develop` is the `nuget.config` file;
in `main` it references the public feed and in `develop` it references the internal feed.
Package references might be different too if `develop` branch is consuming a package that's
not yet on the public feed.

![Example of main and develop branching](images/develop-branch-example.jpg)
Binary file added docs/images/develop-branch-example.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions specs/AppNotifications/AppNotifications-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,53 @@ 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, ico. 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. Below are some examples of usage:

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

std::wstring iconFilepath{ std::filesystem::current_path() / "icon.ico" };
winrt::hstring displayName{ L"AppNotifications" };

manager.Register(displayName, winrt::Windows::Foundation::Uri {iconFilepath});

// 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;
}
```

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

std::wstring iconFilepath{ std::filesystem::current_path() / "icon.ico" };

std::wstring displayName{};
wil::GetModuleFileNameExW(GetCurrentProcess(), nullptr, displayName);

manager.Register(displayName.c_str(), winrt::Windows::Foundation::Uri {iconFilepath});

// 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 +498,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.
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
Loading

0 comments on commit 6149efb

Please sign in to comment.