Merge remote-tracking branch 'origin/master' into dev/split-controls
This commit is contained in:
Коммит
d09c667a0b
|
@ -1,19 +1,21 @@
|
|||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<UseUWP Condition="'$(TargetFramework)' == 'uap10.0' or '$(TargetFramework)' == 'uap10.0.17763' or '$(TargetFramework)' == 'native' or '$(TargetFramework)' == 'net461'">true</UseUWP>
|
||||
<UseUWP Condition="$(TargetFramework.Contains(`uap10.0`)) or '$(TargetFramework)' == 'net461'">true</UseUWP>
|
||||
</PropertyGroup>
|
||||
|
||||
<Choose>
|
||||
<When Condition="!$(TargetFramework.Contains(`uap10.0`)) and '$(TargetFramework)' != 'native' and '$(IsSampleProject)' != 'true'">
|
||||
<!--We'll include signing the Notifications library since we need the DLL signature to match for interop from class libraries to main projects-->
|
||||
<When Condition="(!$(TargetFramework.Contains(`uap10.0`)) and '$(TargetFramework)' != 'native' and '$(IsSampleProject)' != 'true') or $(MSBuildProjectName) == 'Microsoft.Toolkit.Uwp.Notifications'">
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)toolkit.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
|
||||
<Import Project="$(MSBuildThisFileDirectory)build\Windows.Toolkit.Uwp.Build.targets" Condition="'$(UseUWP)' == 'true'"/>
|
||||
|
||||
<!--Exclude Notifications project from this since it sets different min versions than what we want for notifications-->
|
||||
<Import Project="$(MSBuildThisFileDirectory)build\Windows.Toolkit.Uwp.Build.targets" Condition="'$(UseUWP)' == 'true' and $(MSBuildProjectName) != 'Microsoft.Toolkit.Uwp.Notifications'"/>
|
||||
|
||||
<Target Name="AddCommitHashToAssemblyAttributes" BeforeTargets="GetAssemblyAttributes">
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,22 +1,10 @@
|
|||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
|
||||
<PropertyGroup>
|
||||
<!--<TargetFrameworks>netstandard1.4;uap10.0;native;net461;netcoreapp3.1</TargetFrameworks>-->
|
||||
<!-- Removed 'native' target to unblock CI on VS 16.8, tied to changes breaking workaround for https://github.com/NuGet/Home/issues/5154 -->
|
||||
<TargetFrameworks>netstandard1.4;uap10.0;net461;netcoreapp3.1</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.4;uap10.0.19041;net461;netcoreapp3.1;net5.0;net5.0-windows10.0.17763.0;native</TargetFrameworks>
|
||||
<DefineConstants>$(DefineConstants);NETFX_CORE</DefineConstants>
|
||||
<Title>Windows Community Toolkit Notifications</Title>
|
||||
<Description>
|
||||
Generate tile, toast, and badge notifications for Windows 10 via code, with the help of IntelliSense.
|
||||
Adds Support for adaptive tiles and adaptive/interactive toasts for Windows 10. It is part of the Windows Community Toolkit.
|
||||
Supports C# and C++ UWP project types.
|
||||
Also works with C# portable class libraries and non-UWP C# projects like server projects.
|
||||
This project contains outputs for netstandard1.4, uap10.0 and native for WinRT.
|
||||
</Description>
|
||||
<PackageTags>notifications win10 windows 10 tile tiles toast toasts badge xml uwp c# csharp c++</PackageTags>
|
||||
<ExtrasImplicitPlatformPackageIsPrivate Condition=" '$(TargetFramework)' == 'native' ">true</ExtrasImplicitPlatformPackageIsPrivate>
|
||||
<DefaultTargetPlatformMinVersion>10240</DefaultTargetPlatformMinVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<NuspecFile>Microsoft.Toolkit.Uwp.Notifications.nuspec</NuspecFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<Choose>
|
||||
|
@ -27,16 +15,17 @@
|
|||
<!--Reference Windows SDK NuGet of correct target platform version-->
|
||||
<PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.19041.1" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!--Define the WINDOWS_UWP conditional symbol, since the Windows.Data.Xml and the Windows.UI.Notification namespaces are available-->
|
||||
<DefineConstants>$(DefineConstants);WINDOWS_UWP;WIN32</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
|
||||
</Choose>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net461' or '$(TargetFramework)'=='netcoreapp3.1' or '$(TargetFramework)'=='net5.0-windows10.0.17763.0'">
|
||||
<!--Define the WINDOWS_UWP conditional symbol, since the Windows.Data.Xml and the Windows.UI.Notification namespaces are available-->
|
||||
<DefineConstants>$(DefineConstants);WINDOWS_UWP;WIN32</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--NET Core desktop apps also need the Registry NuGet package and System.Reflection.Emit for generating COM class dynamically-->
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp3.1'">
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp3.1' or '$(TargetFramework)'=='net5.0-windows10.0.17763.0'">
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
||||
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
|
||||
|
@ -54,6 +43,12 @@
|
|||
<ItemGroup Condition=" '$(TargetFramework)' != 'native' ">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0.19041'">
|
||||
<TargetPlatformVersion>10.0.19041.0</TargetPlatformVersion>
|
||||
<DefaultTargetPlatformMinVersion>16299</DefaultTargetPlatformMinVersion>
|
||||
<TargetPlatformMinVersion>10.0.16299.0</TargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'native'">
|
||||
<OutputType>winmdobj</OutputType>
|
||||
|
@ -63,8 +58,9 @@
|
|||
<NugetTargetMoniker Condition="'$(DesignTimeBuild)' == 'true'">native</NugetTargetMoniker>
|
||||
<NugetTargetMoniker Condition="'$(DesignTimeBuild)' != 'true'">UAP,Version=v10.0</NugetTargetMoniker>
|
||||
<PackageTargetFallback>uap10.0</PackageTargetFallback>
|
||||
<TargetPlatformVersion Condition="'$(TargetPlatformVersion)' == '' ">10.0.19041.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion Condition="'$(TargetPlatformMinVersion)' == '' ">10.0.10240.0</TargetPlatformMinVersion>
|
||||
<TargetPlatformVersion>10.0.19041.0</TargetPlatformVersion>
|
||||
<DefaultTargetPlatformMinVersion>10240</DefaultTargetPlatformMinVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<DefineConstants Condition="'$(DisableImplicitFrameworkDefines)' != 'true'">$(DefineConstants);NETFX_CORE;WINDOWS_UWP;WINRT</DefineConstants>
|
||||
<CopyLocalLockFileAssemblies Condition="'$(CopyLocalLockFileAssemblies)' == ''">false</CopyLocalLockFileAssemblies>
|
||||
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
|
||||
|
@ -76,5 +72,12 @@
|
|||
<ImplicitFrameworkDefine Condition="'$(DisableImplicitFrameworkDefines)' != 'true'">UAP$(TargetPlatformMinVersion.Replace('.', '_'))</ImplicitFrameworkDefine>
|
||||
<DisableImplicitFrameworkDefines Condition="'$(DisableImplicitFrameworkDefines)' != 'true'">true</DisableImplicitFrameworkDefines>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--Set the nuspec properties. Dependent on version which isn't updated till after GetBuildVersion. Condition ensures we only set once since this runs multiple times for each target.-->
|
||||
<Target Name="SetNuspecProperties" AfterTargets="GetBuildVersion">
|
||||
<PropertyGroup Condition="'$(NuspecProperties)' == ''">
|
||||
<NuspecProperties>buildOutput=bin\$(Configuration);version=$(Version)</NuspecProperties>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Microsoft.Toolkit.Uwp.Notifications</id>
|
||||
<version>$version$</version>
|
||||
<title>Windows Community Toolkit Notifications</title>
|
||||
<authors>Microsoft.Toolkit,dotnetfoundation</authors>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<licenseUrl>https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/license.md</licenseUrl>
|
||||
<projectUrl>https://github.com/windows-toolkit/WindowsCommunityToolkit</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/windows-toolkit/WindowsCommunityToolkit/master/build/nuget.png</iconUrl>
|
||||
<description>The official way to send toast notifications on Windows 10 via code rather than XML, with the help of IntelliSense. Supports all C# app types, including WPF, UWP, WinForms, and Console, even without packaging your app as MSIX. Also supports C++ UWP apps.
|
||||
|
||||
Additionally, generate notification payloads from your ASP.NET web server to send as push notifications, or generate notification payloads from class libraries.
|
||||
|
||||
For UWP/MSIX apps, you can also generate tile and badge notifications.</description>
|
||||
<releaseNotes>https://github.com/windows-toolkit/WindowsCommunityToolkit/releases</releaseNotes>
|
||||
<copyright>(c) .NET Foundation and Contributors. All rights reserved.</copyright>
|
||||
<tags>notifications win10 windows 10 tile tiles toast toasts badge xml uwp c# csharp c++ wpf winforms</tags>
|
||||
<repository type="git" url="https://github.com/windows-toolkit/WindowsCommunityToolkit.git" commit="72205c9add7c3fc1ed63bb77e6fc101e39f1ac33" />
|
||||
<dependencies>
|
||||
<group targetFramework=".NETStandard1.4">
|
||||
<dependency id="NETStandard.Library" version="1.6.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework="UAP10.0.16299">
|
||||
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETCoreApp3.1">
|
||||
<dependency id="Microsoft.Win32.Registry" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.Windows.SDK.Contracts" version="10.0.19041.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Drawing.Common" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Reflection.Emit" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework="net5.0">
|
||||
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework="net5.0-windows10.0.17763">
|
||||
<dependency id="Microsoft.Win32.Registry" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Drawing.Common" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Reflection.Emit" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETFramework4.6.1">
|
||||
<dependency id="Microsoft.NETFramework.ReferenceAssemblies" version="1.0.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.Windows.SDK.Contracts" version="10.0.19041.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework="native0.0" />
|
||||
<group targetFramework="UAP10.0.10240" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="Microsoft.Toolkit.Uwp.Notifications.targets" target="build\native\Microsoft.Toolkit.Uwp.Notifications.targets" />
|
||||
<file src="$buildOutput$\net461\Microsoft.Toolkit.Uwp.Notifications.dll" target="lib\net461\Microsoft.Toolkit.Uwp.Notifications.dll" />
|
||||
<file src="$buildOutput$\net461\Microsoft.Toolkit.Uwp.Notifications.pdb" target="lib\net461\Microsoft.Toolkit.Uwp.Notifications.pdb" />
|
||||
<file src="$buildOutput$\net461\Microsoft.Toolkit.Uwp.Notifications.xml" target="lib\net461\Microsoft.Toolkit.Uwp.Notifications.xml" />
|
||||
<file src="$buildOutput$\net5.0\Microsoft.Toolkit.Uwp.Notifications.dll" target="lib\net5.0\Microsoft.Toolkit.Uwp.Notifications.dll" />
|
||||
<file src="$buildOutput$\net5.0\Microsoft.Toolkit.Uwp.Notifications.pdb" target="lib\net5.0\Microsoft.Toolkit.Uwp.Notifications.pdb" />
|
||||
<file src="$buildOutput$\net5.0\Microsoft.Toolkit.Uwp.Notifications.xml" target="lib\net5.0\Microsoft.Toolkit.Uwp.Notifications.xml" />
|
||||
<file src="$buildOutput$\net5.0-windows10.0.17763.0\Microsoft.Toolkit.Uwp.Notifications.dll" target="lib\net5.0-windows10.0.17763\Microsoft.Toolkit.Uwp.Notifications.dll" />
|
||||
<file src="$buildOutput$\net5.0-windows10.0.17763.0\Microsoft.Toolkit.Uwp.Notifications.pdb" target="lib\net5.0-windows10.0.17763\Microsoft.Toolkit.Uwp.Notifications.pdb" />
|
||||
<file src="$buildOutput$\net5.0-windows10.0.17763.0\Microsoft.Toolkit.Uwp.Notifications.xml" target="lib\net5.0-windows10.0.17763\Microsoft.Toolkit.Uwp.Notifications.xml" />
|
||||
<file src="$buildOutput$\netcoreapp3.1\Microsoft.Toolkit.Uwp.Notifications.dll" target="lib\netcoreapp3.1\Microsoft.Toolkit.Uwp.Notifications.dll" />
|
||||
<file src="$buildOutput$\netcoreapp3.1\Microsoft.Toolkit.Uwp.Notifications.pdb" target="lib\netcoreapp3.1\Microsoft.Toolkit.Uwp.Notifications.pdb" />
|
||||
<file src="$buildOutput$\netcoreapp3.1\Microsoft.Toolkit.Uwp.Notifications.xml" target="lib\netcoreapp3.1\Microsoft.Toolkit.Uwp.Notifications.xml" />
|
||||
<file src="$buildOutput$\netstandard1.4\Microsoft.Toolkit.Uwp.Notifications.dll" target="lib\netstandard1.4\Microsoft.Toolkit.Uwp.Notifications.dll" />
|
||||
<file src="$buildOutput$\netstandard1.4\Microsoft.Toolkit.Uwp.Notifications.pdb" target="lib\netstandard1.4\Microsoft.Toolkit.Uwp.Notifications.pdb" />
|
||||
<file src="$buildOutput$\netstandard1.4\Microsoft.Toolkit.Uwp.Notifications.xml" target="lib\netstandard1.4\Microsoft.Toolkit.Uwp.Notifications.xml" />
|
||||
<file src="$buildOutput$\uap10.0.19041\Microsoft.Toolkit.Uwp.Notifications.dll" target="lib\uap10.0.16299\Microsoft.Toolkit.Uwp.Notifications.dll" />
|
||||
<file src="$buildOutput$\uap10.0.19041\Microsoft.Toolkit.Uwp.Notifications.pdb" target="lib\uap10.0.16299\Microsoft.Toolkit.Uwp.Notifications.pdb" />
|
||||
<file src="$buildOutput$\uap10.0.19041\Microsoft.Toolkit.Uwp.Notifications.pri" target="lib\uap10.0.16299\Microsoft.Toolkit.Uwp.Notifications.pri" />
|
||||
<file src="$buildOutput$\uap10.0.19041\Microsoft.Toolkit.Uwp.Notifications.xml" target="lib\uap10.0.16299\Microsoft.Toolkit.Uwp.Notifications.xml" />
|
||||
<file src="$buildOutput$\native\Microsoft.Toolkit.Uwp.Notifications.pdb" target="lib\native\Microsoft.Toolkit.Uwp.Notifications.pdb" />
|
||||
<file src="$buildOutput$\native\Microsoft.Toolkit.Uwp.Notifications.pri" target="lib\native\Microsoft.Toolkit.Uwp.Notifications.pri" />
|
||||
<file src="$buildOutput$\native\Microsoft.Toolkit.Uwp.Notifications.winmd" target="lib\native\Microsoft.Toolkit.Uwp.Notifications.winmd" />
|
||||
<file src="$buildOutput$\native\Microsoft.Toolkit.Uwp.Notifications.xml" target="lib\native\Microsoft.Toolkit.Uwp.Notifications.xml" />
|
||||
<file src="$buildOutput$\native\Microsoft.Toolkit.Uwp.Notifications.pdb" target="lib\uap10.0.10240\Microsoft.Toolkit.Uwp.Notifications.pdb" />
|
||||
<file src="$buildOutput$\native\Microsoft.Toolkit.Uwp.Notifications.pri" target="lib\uap10.0.10240\Microsoft.Toolkit.Uwp.Notifications.pri" />
|
||||
<file src="$buildOutput$\native\Microsoft.Toolkit.Uwp.Notifications.winmd" target="lib\uap10.0.10240\Microsoft.Toolkit.Uwp.Notifications.winmd" />
|
||||
<file src="$buildOutput$\native\Microsoft.Toolkit.Uwp.Notifications.xml" target="lib\uap10.0.10240\Microsoft.Toolkit.Uwp.Notifications.xml" />
|
||||
</files>
|
||||
</package>
|
|
@ -20,9 +20,6 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
{
|
||||
private const long APPMODEL_ERROR_NO_PACKAGE = 15700L;
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName);
|
||||
|
||||
private static bool? _hasIdentity;
|
||||
|
||||
public static bool HasIdentity()
|
||||
|
@ -37,10 +34,10 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
{
|
||||
int length = 0;
|
||||
var sb = new StringBuilder(0);
|
||||
GetCurrentPackageFullName(ref length, sb);
|
||||
NativeMethods.GetCurrentPackageFullName(ref length, sb);
|
||||
|
||||
sb = new StringBuilder(length);
|
||||
int error = GetCurrentPackageFullName(ref length, sb);
|
||||
int error = NativeMethods.GetCurrentPackageFullName(ref length, sb);
|
||||
|
||||
_hasIdentity = error != APPMODEL_ERROR_NO_PACKAGE;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
}
|
||||
|
||||
// If running as Desktop Bridge
|
||||
if (DesktopBridgeHelpers.IsRunningAsUwp())
|
||||
if (DesktopBridgeHelpers.HasIdentity())
|
||||
{
|
||||
// Clear the AUMID since Desktop Bridge doesn't use it, and then we're done.
|
||||
// Desktop Bridge apps are registered with platform through their manifest.
|
||||
|
@ -94,7 +94,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
// Big thanks to FrecherxDachs for figuring out the following code which works in .NET Core 3: https://github.com/FrecherxDachs/UwpNotificationNetCoreTest
|
||||
var uuid = typeof(T).GUID;
|
||||
uint cookie;
|
||||
CoRegisterClassObject(
|
||||
NativeMethods.CoRegisterClassObject(
|
||||
uuid,
|
||||
new NotificationActivatorClassFactory<T>(),
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
|
@ -151,14 +151,6 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
}
|
||||
}
|
||||
|
||||
[DllImport("ole32.dll")]
|
||||
private static extern int CoRegisterClassObject(
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
|
||||
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
|
||||
uint dwClsContext,
|
||||
uint flags,
|
||||
out uint lpdwRegister);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toast notifier. You must have called <see cref="RegisterActivator{T}"/> first (and also <see cref="RegisterAumidAndComServer(string)"/> if you're a classic Win32 app), or this will throw an exception.
|
||||
/// </summary>
|
||||
|
@ -198,7 +190,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
if (!_registeredAumidAndComServer)
|
||||
{
|
||||
// Check if Desktop Bridge
|
||||
if (DesktopBridgeHelpers.IsRunningAsUwp())
|
||||
if (DesktopBridgeHelpers.HasIdentity())
|
||||
{
|
||||
// Implicitly registered, all good!
|
||||
_registeredAumidAndComServer = true;
|
||||
|
@ -223,55 +215,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
/// </summary>
|
||||
public static bool CanUseHttpImages
|
||||
{
|
||||
get { return DesktopBridgeHelpers.IsRunningAsUwp(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Code from https://github.com/qmatteoq/DesktopBridgeHelpers/tree/master/DesktopBridge.Helpers/Helpers.cs
|
||||
/// </summary>
|
||||
private class DesktopBridgeHelpers
|
||||
{
|
||||
private const long APPMODEL_ERROR_NO_PACKAGE = 15700L;
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName);
|
||||
|
||||
private static bool? _isRunningAsUwp;
|
||||
|
||||
public static bool IsRunningAsUwp()
|
||||
{
|
||||
if (_isRunningAsUwp == null)
|
||||
{
|
||||
if (IsWindows7OrLower)
|
||||
{
|
||||
_isRunningAsUwp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int length = 0;
|
||||
var sb = new StringBuilder(0);
|
||||
GetCurrentPackageFullName(ref length, sb);
|
||||
|
||||
sb = new StringBuilder(length);
|
||||
int error = GetCurrentPackageFullName(ref length, sb);
|
||||
|
||||
_isRunningAsUwp = error != APPMODEL_ERROR_NO_PACKAGE;
|
||||
}
|
||||
}
|
||||
|
||||
return _isRunningAsUwp.Value;
|
||||
}
|
||||
|
||||
private static bool IsWindows7OrLower
|
||||
{
|
||||
get
|
||||
{
|
||||
int versionMajor = Environment.OSVersion.Version.Major;
|
||||
int versionMinor = Environment.OSVersion.Version.Minor;
|
||||
double version = versionMajor + ((double)versionMinor / 10);
|
||||
return version <= 6.1;
|
||||
}
|
||||
}
|
||||
get { return DesktopBridgeHelpers.HasIdentity(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#if WIN32
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Notifications
|
||||
{
|
||||
internal class NativeMethods
|
||||
{
|
||||
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool DeleteObject([In] IntPtr hObject);
|
||||
|
||||
[DllImport("ole32.dll")]
|
||||
internal static extern int CoRegisterClassObject(
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
|
||||
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
|
||||
uint dwClsContext,
|
||||
uint flags,
|
||||
out uint lpdwRegister);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -5,10 +5,12 @@
|
|||
#if WIN32
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
|
@ -18,6 +20,11 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
{
|
||||
internal class Win32AppInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// If an AUMID is greater than 129 characters, scheduled toast notification APIs will throw an exception.
|
||||
/// </summary>
|
||||
private const int AUMID_MAX_LENGTH = 129;
|
||||
|
||||
public string Aumid { get; set; }
|
||||
|
||||
public string DisplayName { get; set; }
|
||||
|
@ -32,6 +39,9 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
IApplicationResolver appResolver = (IApplicationResolver)new CAppResolver();
|
||||
appResolver.GetAppIDForProcess(Convert.ToUInt32(process.Id), out string appId, out _, out _, out _);
|
||||
|
||||
// Use app ID (or hashed app ID) as AUMID
|
||||
string aumid = appId.Length > AUMID_MAX_LENGTH ? HashAppId(appId) : appId;
|
||||
|
||||
// Then try to get the shortcut (for display name and icon)
|
||||
IShellItem shortcutItem = null;
|
||||
try
|
||||
|
@ -74,7 +84,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
{
|
||||
}
|
||||
|
||||
DeleteObject(nativeHBitmap);
|
||||
NativeMethods.DeleteObject(nativeHBitmap);
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
@ -98,12 +108,21 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
|
||||
return new Win32AppInfo()
|
||||
{
|
||||
Aumid = appId,
|
||||
Aumid = aumid,
|
||||
DisplayName = displayName,
|
||||
IconPath = iconPath
|
||||
};
|
||||
}
|
||||
|
||||
private static string HashAppId(string appId)
|
||||
{
|
||||
using (SHA1 sha1 = SHA1.Create())
|
||||
{
|
||||
byte[] hashedBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(appId));
|
||||
return string.Join(string.Empty, hashedBytes.Select(b => b.ToString("X2")));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetDisplayNameFromCurrentProcess(Process process)
|
||||
{
|
||||
// If AssemblyTitle is set, use that
|
||||
|
@ -212,10 +231,6 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
return false;
|
||||
}
|
||||
|
||||
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool DeleteObject([In] IntPtr hObject);
|
||||
|
||||
/// <summary>
|
||||
/// From https://stackoverflow.com/a/41622689/1454643
|
||||
/// Generates Guid based on String. Key assumption for this algorithm is that name is unique (across where it it's being used)
|
||||
|
|
|
@ -53,14 +53,18 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||
public void Remove(string tag)
|
||||
{
|
||||
#if WIN32
|
||||
if (_aumid != null)
|
||||
{
|
||||
_history.Remove(tag, string.Empty, _aumid);
|
||||
_history.Remove(tag, ToastNotificationManagerCompat.DEFAULT_GROUP, _aumid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_history.Remove(tag);
|
||||
}
|
||||
#else
|
||||
_history.Remove(tag);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
{
|
||||
#if WIN32
|
||||
private const string TOAST_ACTIVATED_LAUNCH_ARG = "-ToastActivated";
|
||||
private const string REG_HAS_SENT_NOTIFICATION = "HasSentNotification";
|
||||
internal const string DEFAULT_GROUP = "toolkitGroupNull";
|
||||
|
||||
private const int CLASS_E_NOAGGREGATION = -2147221232;
|
||||
private const int E_NOINTERFACE = -2147467262;
|
||||
|
@ -37,6 +39,8 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
private static bool _registeredOnActivated;
|
||||
private static List<OnActivated> _onActivated = new List<OnActivated>();
|
||||
|
||||
private static bool _hasSentNotification;
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered when a notification or notification button is clicked. Subscribe to this event in your app's initial startup code.
|
||||
/// </summary>
|
||||
|
@ -55,7 +59,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_initializeEx = ex;
|
||||
_initializeEx = new InvalidOperationException("Failed to register notification activator", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +108,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
|
||||
private static string _win32Aumid;
|
||||
private static string _clsid;
|
||||
private static Exception _initializeEx;
|
||||
private static InvalidOperationException _initializeEx;
|
||||
|
||||
static ToastNotificationManagerCompat()
|
||||
{
|
||||
|
@ -115,7 +119,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
catch (Exception ex)
|
||||
{
|
||||
// We catch the exception so that things like subscribing to the event handler doesn't crash app
|
||||
_initializeEx = ex;
|
||||
_initializeEx = new InvalidOperationException("Failed initializing notifications", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +149,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
var activatorType = CreateAndRegisterActivator();
|
||||
|
||||
// Register via registry
|
||||
using (var rootKey = Registry.CurrentUser.CreateSubKey(@"Software\Classes\AppUserModelId\" + _win32Aumid))
|
||||
using (var rootKey = Registry.CurrentUser.CreateSubKey(GetRegistrySubKey()))
|
||||
{
|
||||
// If they don't have identity, we need to specify the display assets
|
||||
if (!DesktopBridgeHelpers.HasIdentity())
|
||||
|
@ -168,12 +172,20 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
// Background color only appears in the settings page, format is
|
||||
// hex without leading #, like "FFDDDDDD"
|
||||
rootKey.SetValue("IconBackgroundColor", "FFDDDDDD");
|
||||
|
||||
// Additionally, we need to read whether they've sent a notification before
|
||||
_hasSentNotification = rootKey.GetValue(REG_HAS_SENT_NOTIFICATION) != null;
|
||||
}
|
||||
|
||||
rootKey.SetValue("CustomActivator", string.Format("{{{0}}}", activatorType.GUID));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetRegistrySubKey()
|
||||
{
|
||||
return @"Software\Classes\AppUserModelId\" + _win32Aumid;
|
||||
}
|
||||
|
||||
private static Type CreateActivatorType()
|
||||
{
|
||||
// https://stackoverflow.com/questions/24069352/c-sharp-typebuilder-generate-class-with-function-dynamically
|
||||
|
@ -239,7 +251,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
|
||||
// Big thanks to FrecherxDachs for figuring out the following code which works in .NET Core 3: https://github.com/FrecherxDachs/UwpNotificationNetCoreTest
|
||||
var uuid = activatorType.GUID;
|
||||
CoRegisterClassObject(
|
||||
NativeMethods.CoRegisterClassObject(
|
||||
uuid,
|
||||
new NotificationActivatorClassFactory(activatorType),
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
|
@ -250,12 +262,36 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
private static void RegisterComServer(Type activatorType, string exePath)
|
||||
{
|
||||
// We register the EXE to start up when the notification is activated
|
||||
string regString = string.Format("SOFTWARE\\Classes\\CLSID\\{{{0}}}\\LocalServer32", activatorType.GUID);
|
||||
var key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(regString);
|
||||
string regString = string.Format("SOFTWARE\\Classes\\CLSID\\{{{0}}}", activatorType.GUID);
|
||||
using (var key = Registry.CurrentUser.CreateSubKey(regString + "\\LocalServer32"))
|
||||
{
|
||||
// Include a flag so we know this was a toast activation and should wait for COM to process
|
||||
// We also wrap EXE path in quotes for extra security
|
||||
key.SetValue(null, '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG);
|
||||
}
|
||||
|
||||
// Include a flag so we know this was a toast activation and should wait for COM to process
|
||||
// We also wrap EXE path in quotes for extra security
|
||||
key.SetValue(null, '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG);
|
||||
if (IsElevated)
|
||||
{
|
||||
//// For elevated apps, we need to ensure they'll activate in existing running process by adding
|
||||
//// some values in local machine
|
||||
using (var key = Registry.LocalMachine.CreateSubKey(regString))
|
||||
{
|
||||
// Same as above, except also including AppId to link to our AppId entry below
|
||||
using (var localServer32 = key.CreateSubKey("LocalServer32"))
|
||||
{
|
||||
localServer32.SetValue(null, '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG);
|
||||
}
|
||||
|
||||
key.SetValue("AppId", "{" + activatorType.GUID + "}");
|
||||
}
|
||||
|
||||
// This tells COM to match any client, so Action Center will activate our elevated process.
|
||||
// More info: https://docs.microsoft.com/windows/win32/com/runas
|
||||
using (var key = Registry.LocalMachine.CreateSubKey(string.Format("SOFTWARE\\Classes\\AppID\\{{{0}}}", activatorType.GUID)))
|
||||
{
|
||||
key.SetValue("RunAs", "Interactive User");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -320,20 +356,20 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
}
|
||||
}
|
||||
|
||||
[DllImport("ole32.dll")]
|
||||
private static extern int CoRegisterClassObject(
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
|
||||
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
|
||||
uint dwClsContext,
|
||||
uint flags,
|
||||
out uint lpdwRegister);
|
||||
private static bool IsElevated
|
||||
{
|
||||
get
|
||||
{
|
||||
return new System.Security.Principal.WindowsPrincipal(System.Security.Principal.WindowsIdentity.GetCurrent()).IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toast notifier.
|
||||
/// </summary>
|
||||
/// <returns><see cref="ToastNotifier"/></returns>
|
||||
public static ToastNotifier CreateToastNotifier()
|
||||
/// <returns><see cref="ToastNotifierCompat"/>An instance of the toast notifier.</returns>
|
||||
public static ToastNotifierCompat CreateToastNotifier()
|
||||
{
|
||||
#if WIN32
|
||||
if (_initializeEx != null)
|
||||
|
@ -343,14 +379,14 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
|
||||
if (DesktopBridgeHelpers.HasIdentity())
|
||||
{
|
||||
return ToastNotificationManager.CreateToastNotifier();
|
||||
return new ToastNotifierCompat(ToastNotificationManager.CreateToastNotifier());
|
||||
}
|
||||
else
|
||||
{
|
||||
return ToastNotificationManager.CreateToastNotifier(_win32Aumid);
|
||||
return new ToastNotifierCompat(ToastNotificationManager.CreateToastNotifier(_win32Aumid));
|
||||
}
|
||||
#else
|
||||
return ToastNotificationManager.CreateToastNotifier();
|
||||
return new ToastNotifierCompat(ToastNotificationManager.CreateToastNotifier());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -437,7 +473,7 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
// Remove registry key
|
||||
if (_win32Aumid != null)
|
||||
{
|
||||
Registry.CurrentUser.DeleteSubKey(@"Software\Classes\AppUserModelId\" + _win32Aumid);
|
||||
Registry.CurrentUser.DeleteSubKey(GetRegistrySubKey());
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
@ -448,7 +484,32 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
{
|
||||
if (_clsid != null)
|
||||
{
|
||||
Registry.CurrentUser.DeleteSubKey(string.Format("SOFTWARE\\Classes\\CLSID\\{{{0}}}\\LocalServer32", _clsid));
|
||||
try
|
||||
{
|
||||
Registry.CurrentUser.DeleteSubKeyTree(string.Format("SOFTWARE\\Classes\\CLSID\\{{{0}}}", _clsid));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (IsElevated)
|
||||
{
|
||||
try
|
||||
{
|
||||
Registry.LocalMachine.DeleteSubKeyTree(string.Format("SOFTWARE\\Classes\\CLSID\\{{{0}}}", _clsid));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Registry.LocalMachine.DeleteSubKeyTree(string.Format("SOFTWARE\\Classes\\AppID\\{{{0}}}", _clsid));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
@ -472,6 +533,51 @@ namespace Microsoft.Toolkit.Uwp.Notifications
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WIN32
|
||||
internal static void SetHasSentToastNotification()
|
||||
{
|
||||
// For plain Win32 apps, record that we've sent a notification
|
||||
if (!_hasSentNotification && !DesktopBridgeHelpers.HasIdentity())
|
||||
{
|
||||
_hasSentNotification = true;
|
||||
|
||||
try
|
||||
{
|
||||
using (var rootKey = Registry.CurrentUser.CreateSubKey(GetRegistrySubKey()))
|
||||
{
|
||||
rootKey.SetValue(REG_HAS_SENT_NOTIFICATION, 1);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void PreRegisterIdentityLessApp()
|
||||
{
|
||||
// For plain Win32 apps, we first have to have send a toast notification once before using scheduled toasts.
|
||||
if (!_hasSentNotification && !DesktopBridgeHelpers.HasIdentity())
|
||||
{
|
||||
const string tag = "toolkit1stNotif";
|
||||
|
||||
// Show the toast
|
||||
new ToastContentBuilder()
|
||||
.AddText("New notification")
|
||||
.Show(toast =>
|
||||
{
|
||||
// We'll hide the popup and set the toast to expire in case removing doesn't work
|
||||
toast.SuppressPopup = true;
|
||||
toast.Tag = tag;
|
||||
toast.ExpirationTime = DateTime.Now.AddSeconds(15);
|
||||
});
|
||||
|
||||
// And then remove it
|
||||
ToastNotificationManagerCompat.History.Remove(tag);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#if WINDOWS_UWP
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Notifications;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Notifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows you to show and schedule toast notifications.
|
||||
/// </summary>
|
||||
public sealed class ToastNotifierCompat
|
||||
{
|
||||
private ToastNotifier _notifier;
|
||||
|
||||
internal ToastNotifierCompat(ToastNotifier notifier)
|
||||
{
|
||||
_notifier = notifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays the specified toast notification.
|
||||
/// </summary>
|
||||
/// <param name="notification">The object that contains the content of the toast notification to display.</param>
|
||||
public void Show(ToastNotification notification)
|
||||
{
|
||||
#if WIN32
|
||||
PreprocessToast(notification);
|
||||
#endif
|
||||
|
||||
_notifier.Show(notification);
|
||||
|
||||
#if WIN32
|
||||
ToastNotificationManagerCompat.SetHasSentToastNotification();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the specified toast notification from the screen (moves it into Action Center).
|
||||
/// </summary>
|
||||
/// <param name="notification">The object that specifies the toast to hide.</param>
|
||||
public void Hide(ToastNotification notification)
|
||||
{
|
||||
#if WIN32
|
||||
PreprocessToast(notification);
|
||||
#endif
|
||||
|
||||
_notifier.Hide(notification);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a ScheduledToastNotification for later display by Windows.
|
||||
/// </summary>
|
||||
/// <param name="scheduledToast">The scheduled toast notification, which includes its content and timing instructions.</param>
|
||||
public void AddToSchedule(ScheduledToastNotification scheduledToast)
|
||||
{
|
||||
#if WIN32
|
||||
ToastNotificationManagerCompat.PreRegisterIdentityLessApp();
|
||||
|
||||
PreprocessScheduledToast(scheduledToast);
|
||||
#endif
|
||||
|
||||
_notifier.AddToSchedule(scheduledToast);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the scheduled display of a specified ScheduledToastNotification.
|
||||
/// </summary>
|
||||
/// <param name="scheduledToast">The notification to remove from the schedule.</param>
|
||||
public void RemoveFromSchedule(ScheduledToastNotification scheduledToast)
|
||||
{
|
||||
#if WIN32
|
||||
PreprocessScheduledToast(scheduledToast);
|
||||
#endif
|
||||
|
||||
_notifier.RemoveFromSchedule(scheduledToast);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of ScheduledToastNotification objects that this app has scheduled for display.
|
||||
/// </summary>
|
||||
/// <returns>The collection of scheduled toast notifications that the app bound to this notifier has scheduled for timed display.</returns>
|
||||
public IReadOnlyList<ScheduledToastNotification> GetScheduledToastNotifications()
|
||||
{
|
||||
return _notifier.GetScheduledToastNotifications();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the existing toast notification that has the specified tag and belongs to the specified notification group.
|
||||
/// </summary>
|
||||
/// <param name="data">An object that contains the updated info.</param>
|
||||
/// <param name="tag">The identifier of the toast notification to update.</param>
|
||||
/// <param name="group">The ID of the ToastCollection that contains the notification.</param>
|
||||
/// <returns>A value that indicates the result of the update (failure, success, etc).</returns>
|
||||
public NotificationUpdateResult Update(NotificationData data, string tag, string group)
|
||||
{
|
||||
return _notifier.Update(data, tag, group);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the existing toast notification that has the specified tag.
|
||||
/// </summary>
|
||||
/// <param name="data">An object that contains the updated info.</param>
|
||||
/// <param name="tag">The identifier of the toast notification to update.</param>
|
||||
/// <returns>A value that indicates the result of the update (failure, success, etc).</returns>
|
||||
public NotificationUpdateResult Update(NotificationData data, string tag)
|
||||
{
|
||||
#if WIN32
|
||||
// For apps that don't have identity...
|
||||
if (!DesktopBridgeHelpers.HasIdentity())
|
||||
{
|
||||
// If group isn't specified, we have to add a group since otherwise can't remove without a group
|
||||
return Update(data, tag, ToastNotificationManagerCompat.DEFAULT_GROUP);
|
||||
}
|
||||
#endif
|
||||
|
||||
return _notifier.Update(data, tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that tells you whether there is an app, user, or system block that prevents the display of a toast notification.
|
||||
/// </summary>
|
||||
public NotificationSetting Setting
|
||||
{
|
||||
get
|
||||
{
|
||||
#if WIN32
|
||||
// Just like scheduled notifications, apps need to have sent a notification
|
||||
// before checking the setting value works
|
||||
ToastNotificationManagerCompat.PreRegisterIdentityLessApp();
|
||||
#endif
|
||||
|
||||
return _notifier.Setting;
|
||||
}
|
||||
}
|
||||
|
||||
#if WIN32
|
||||
private void PreprocessToast(ToastNotification notification)
|
||||
{
|
||||
// For apps that don't have identity...
|
||||
if (!DesktopBridgeHelpers.HasIdentity())
|
||||
{
|
||||
// If tag is specified
|
||||
if (!string.IsNullOrEmpty(notification.Tag))
|
||||
{
|
||||
// If group isn't specified, we have to add a group since otherwise can't remove without a group
|
||||
notification.Group = ToastNotificationManagerCompat.DEFAULT_GROUP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PreprocessScheduledToast(ScheduledToastNotification notification)
|
||||
{
|
||||
// For apps that don't have identity...
|
||||
if (!DesktopBridgeHelpers.HasIdentity())
|
||||
{
|
||||
// If tag is specified
|
||||
if (!string.IsNullOrEmpty(notification.Tag))
|
||||
{
|
||||
// If group isn't specified, we have to add a group since otherwise can't remove without a group
|
||||
notification.Group = ToastNotificationManagerCompat.DEFAULT_GROUP;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -28,8 +28,7 @@
|
|||
<TextBlock HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Foreground="OrangeRed"
|
||||
IsHitTestVisible="False"
|
||||
Text="Please scroll down to see the effect." />
|
||||
IsHitTestVisible="False"><Run Text="Please scroll down to see the effect." /><LineBreak /><Run Text="The default threshold for triggering lazy loading is 300 px." /></TextBlock>
|
||||
<Button Width="48"
|
||||
Height="48"
|
||||
HorizontalAlignment="Right"
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="LazyLoadingThreshold" Value="300" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:ImageEx">
|
||||
|
|
|
@ -48,7 +48,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// <summary>
|
||||
/// Identifies the <see cref="EnableLazyLoading"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty EnableLazyLoadingProperty = DependencyProperty.Register(nameof(EnableLazyLoading), typeof(bool), typeof(ImageExBase), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty EnableLazyLoadingProperty = DependencyProperty.Register(nameof(EnableLazyLoading), typeof(bool), typeof(ImageExBase), new PropertyMetadata(false, EnableLazyLoadingChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="LazyLoadingThreshold"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty LazyLoadingThresholdProperty = DependencyProperty.Register(nameof(LazyLoadingThreshold), typeof(double), typeof(ImageExBase), new PropertyMetadata(default(double), LazyLoadingThresholdChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a mask that represents the alpha channel of an image as a <see cref="CompositionBrush"/>
|
||||
|
@ -139,5 +144,40 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
get { return (bool)GetValue(EnableLazyLoadingProperty); }
|
||||
set { SetValue(EnableLazyLoadingProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the threshold for triggering lazy loading.
|
||||
/// </summary>
|
||||
public double LazyLoadingThreshold
|
||||
{
|
||||
get { return (double)GetValue(LazyLoadingThresholdProperty); }
|
||||
set { SetValue(LazyLoadingThresholdProperty, value); }
|
||||
}
|
||||
|
||||
private static void EnableLazyLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ImageExBase control)
|
||||
{
|
||||
var value = (bool)e.NewValue;
|
||||
if (value)
|
||||
{
|
||||
control.LayoutUpdated += control.ImageExBase_LayoutUpdated;
|
||||
|
||||
control.InvalidateLazyLoading();
|
||||
}
|
||||
else
|
||||
{
|
||||
control.LayoutUpdated -= control.ImageExBase_LayoutUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ImageExBase control && control.EnableLazyLoading)
|
||||
{
|
||||
control.InvalidateLazyLoading();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Toolkit.Uwp.Extensions;
|
||||
using Microsoft.Toolkit.Uwp.UI.Extensions;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
@ -79,8 +82,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
public ImageExBase()
|
||||
{
|
||||
LockObj = new object();
|
||||
|
||||
EffectiveViewportChanged += ImageExBase_EffectiveViewportChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -215,15 +216,47 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
VisualStateManager.GoToState(this, FailedState, true);
|
||||
}
|
||||
|
||||
private void ImageExBase_EffectiveViewportChanged(FrameworkElement sender, EffectiveViewportChangedEventArgs args)
|
||||
private void ImageExBase_LayoutUpdated(object sender, object e)
|
||||
{
|
||||
var bringIntoViewDistanceX = args.BringIntoViewDistanceX;
|
||||
var bringIntoViewDistanceY = args.BringIntoViewDistanceY;
|
||||
InvalidateLazyLoading();
|
||||
}
|
||||
|
||||
var width = ActualWidth;
|
||||
var height = ActualHeight;
|
||||
private void InvalidateLazyLoading()
|
||||
{
|
||||
if (!IsLoaded)
|
||||
{
|
||||
_isInViewport = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bringIntoViewDistanceX <= width && bringIntoViewDistanceY <= height)
|
||||
// Find the first ascendant ScrollViewer, if not found, use the root element.
|
||||
FrameworkElement hostElement = null;
|
||||
var ascendants = this.FindAscendants().OfType<FrameworkElement>();
|
||||
foreach (var ascendant in ascendants)
|
||||
{
|
||||
hostElement = ascendant;
|
||||
if (hostElement is ScrollViewer)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hostElement == null)
|
||||
{
|
||||
_isInViewport = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var controlRect = TransformToVisual(hostElement)
|
||||
.TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight));
|
||||
var lazyLoadingThreshold = LazyLoadingThreshold;
|
||||
var hostRect = new Rect(
|
||||
0 - lazyLoadingThreshold,
|
||||
0 - lazyLoadingThreshold,
|
||||
hostElement.ActualWidth + (2 * lazyLoadingThreshold),
|
||||
hostElement.ActualHeight + (2 * lazyLoadingThreshold));
|
||||
|
||||
if (controlRect.IntersectsWith(hostRect))
|
||||
{
|
||||
_isInViewport = true;
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Rect = Windows.Foundation.Rect;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for the <see cref="Rect"/> type.
|
||||
/// </summary>
|
||||
public static class RectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if a rectangle intersects with another rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect1">The first rectangle to test.</param>
|
||||
/// <param name="rect2">The second rectangle to test.</param>
|
||||
/// <returns>This method returns <see langword="true"/> if there is any intersection, otherwise <see langword="false"/>.</returns>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IntersectsWith(this Rect rect1, Rect rect2)
|
||||
{
|
||||
if (rect1.IsEmpty || rect2.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (rect1.Left <= rect2.Right) &&
|
||||
(rect1.Right >= rect2.Left) &&
|
||||
(rect1.Top <= rect2.Bottom) &&
|
||||
(rect1.Bottom >= rect2.Top);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,27 +8,48 @@
|
|||
Title="MainWindow" Height="450" Width="800">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="80"/>
|
||||
<RowDefinition Height="AUto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Margin="20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
x:Name="ButtonPopToast"
|
||||
Content="Pop toast"
|
||||
Click="ButtonPopToast_Click"
|
||||
Margin="0,0,6,0"/>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
x:Name="ButtonClearToasts"
|
||||
Content="Clear toasts"
|
||||
Click="ButtonClearToasts_Click"
|
||||
Margin="6,0,0,0"/>
|
||||
</Grid>
|
||||
<StackPanel Margin="20">
|
||||
<Grid Height="36">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
x:Name="ButtonPopToast"
|
||||
Content="Pop toast"
|
||||
Click="ButtonPopToast_Click"
|
||||
Margin="0,0,6,0"/>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
x:Name="ButtonScheduleToast"
|
||||
Content="Schedule toast"
|
||||
Click="ButtonScheduleToast_Click"
|
||||
Margin="6,0,6,0"/>
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
x:Name="ButtonClearToasts"
|
||||
Content="Clear toasts"
|
||||
Click="ButtonClearToasts_Click"
|
||||
Margin="6,0,0,0"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,12,0,0" Height="36">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
x:Name="ButtonProgressToast"
|
||||
Content="Progress bar toast"
|
||||
Click="ButtonProgressToast_Click"
|
||||
Margin="0,0,6,0"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<ContentControl
|
||||
x:Name="ContentBody"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -10,6 +11,8 @@ using System.Windows;
|
|||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using Windows.Services.Maps;
|
||||
using Windows.UI.Notifications;
|
||||
|
||||
namespace Microsoft.Toolkit.Win32.WpfCore.SampleApp
|
||||
{
|
||||
|
@ -27,6 +30,12 @@ namespace Microsoft.Toolkit.Win32.WpfCore.SampleApp
|
|||
|
||||
private async void ButtonPopToast_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ToastNotificationManagerCompat.CreateToastNotifier().Setting != NotificationSetting.Enabled)
|
||||
{
|
||||
MessageBox.Show("Notifications are disabled from the system settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
string title = "Andrew sent you a picture";
|
||||
string content = "Check this out, The Enchantments!";
|
||||
string image = "https://picsum.photos/364/202?image=883";
|
||||
|
@ -144,5 +153,84 @@ namespace Microsoft.Toolkit.Win32.WpfCore.SampleApp
|
|||
{
|
||||
ToastNotificationManagerCompat.History.Clear();
|
||||
}
|
||||
|
||||
private async void ButtonScheduleToast_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Schedule a toast to appear in 5 seconds
|
||||
new ToastContentBuilder()
|
||||
|
||||
// Arguments that are returned when the user clicks the toast or a button
|
||||
.AddArgument("action", MyToastActions.ViewConversation)
|
||||
.AddArgument("conversationId", 7764)
|
||||
|
||||
.AddText("Scheduled toast notification")
|
||||
|
||||
.Schedule(DateTime.Now.AddSeconds(5));
|
||||
|
||||
// Inform the user
|
||||
var tb = new TextBlock()
|
||||
{
|
||||
Text = "Toast scheduled to appear in 5 seconds",
|
||||
FontWeight = FontWeights.Bold
|
||||
};
|
||||
|
||||
ContentBody.Content = tb;
|
||||
|
||||
// And after 5 seconds, clear the informational message
|
||||
await Task.Delay(5000);
|
||||
|
||||
if (ContentBody.Content == tb)
|
||||
{
|
||||
ContentBody.Content = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void ButtonProgressToast_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
const string tag = "progressToast";
|
||||
|
||||
new ToastContentBuilder()
|
||||
.AddArgument("action", MyToastActions.ViewConversation)
|
||||
.AddArgument("conversationId", 423)
|
||||
.AddText("Sending image to conversation...")
|
||||
.AddVisualChild(new AdaptiveProgressBar()
|
||||
{
|
||||
Value = new BindableProgressBarValue("progress"),
|
||||
Status = "Sending..."
|
||||
})
|
||||
.Show(toast =>
|
||||
{
|
||||
toast.Tag = tag;
|
||||
|
||||
toast.Data = new NotificationData(new Dictionary<string, string>()
|
||||
{
|
||||
{ "progress", "0" }
|
||||
});
|
||||
});
|
||||
|
||||
double progress = 0;
|
||||
|
||||
while (progress < 1)
|
||||
{
|
||||
await Task.Delay(new Random().Next(1000, 3000));
|
||||
|
||||
progress += (new Random().NextDouble() * 0.15) + 0.1;
|
||||
|
||||
ToastNotificationManagerCompat.CreateToastNotifier().Update(
|
||||
new NotificationData(new Dictionary<string, string>()
|
||||
{
|
||||
{ "progress", progress.ToString() }
|
||||
}), tag);
|
||||
}
|
||||
|
||||
new ToastContentBuilder()
|
||||
.AddArgument("action", MyToastActions.ViewConversation)
|
||||
.AddArgument("conversationId", 423)
|
||||
.AddText("Sent image to conversation!")
|
||||
.Show(toast =>
|
||||
{
|
||||
toast.Tag = tag;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.19041.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.Uwp.Notifications\Microsoft.Toolkit.Uwp.Notifications.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,14 +1,34 @@
|
|||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference="SilentlyContinue"
|
||||
$global:ErrorActionPreference = "Stop"
|
||||
$global:ProgressPreference="SilentlyContinue"
|
||||
|
||||
Write-Host "Running Smoke Test Package Analyis"
|
||||
Write-Host "----------------------------------"
|
||||
|
||||
#####
|
||||
# This script analyzes the final packaged outputs of
|
||||
# There are two things analyzed:
|
||||
# - The MSIX UPLOAD package has the raw dependencies before .NET Native compilation,
|
||||
# this can help determine the maximum impact of the package if everything were to be included.
|
||||
# - The MSIX BUNDLE package is the final .NET Native compiled app,
|
||||
# this can be used to help determine the minimal impact of the package if only a few things are used.
|
||||
# This is also what would end up on a user's machine and be the footprint of the app itself.
|
||||
#
|
||||
# Note: The 'minimum impact' can be larger than the 'maximum impact' as there are many
|
||||
# system dependencies which don't get included by default and are then referenced by a package.
|
||||
# This may seem counter-intuitive at first, but it is important to remember that when combining
|
||||
# package use along with the use of other platform APIs smooths these sizes out as they will
|
||||
# all be shared across all use cases.
|
||||
#####
|
||||
|
||||
# Our script is at our SmokeTest root, we want to look for the AppPackages folder
|
||||
$PackagePath = $PSScriptRoot + "\AppPackages\"
|
||||
$FilePattern = "SmokeTest_{0}_x86_bundle.msixupload"
|
||||
$DirPattern = "SmokeTest_{0}_Test\SmokeTest_{0}_x86.msixbundle"
|
||||
|
||||
$BaselineName = $FilePattern -f "UWPBaseline"
|
||||
$BaselineBundleName = $DirPattern -f "UWPBaseline"
|
||||
$TempFolder = "ExplodedArchive"
|
||||
$TempFolder2 = "ExplodedBundle"
|
||||
|
||||
function Expand-MsixUploadPackage {
|
||||
param (
|
||||
|
@ -47,6 +67,34 @@ function Expand-MsixUploadPackage {
|
|||
Pop-Location
|
||||
}
|
||||
|
||||
function Expand-MsixBundlePackage {
|
||||
param (
|
||||
[string]$PackageFile,
|
||||
[string]$Destination
|
||||
)
|
||||
|
||||
$ZipBundle = $PackageFile.Replace("msixbundle", "zip")
|
||||
|
||||
Move-Item $PackageFile -Destination $ZipBundle
|
||||
|
||||
Expand-Archive $ZipBundle -DestinationPath $Destination
|
||||
|
||||
Move-Item $ZipBundle -Destination $PackageFile
|
||||
|
||||
Push-Location $Destination
|
||||
|
||||
$msix = (Get-ChildItem "*.msix").Name
|
||||
$ZipMSIX = $msix.Replace("msix", "zip")
|
||||
|
||||
Move-Item $msix -Destination $ZipMSIX
|
||||
|
||||
Expand-Archive $ZipMSIX -DestinationPath . -Force # Force here as we have some duplicate file names we don't really care about from parent archives
|
||||
|
||||
Remove-Item $ZipMSIX
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
if (Test-Path $PackagePath)
|
||||
{
|
||||
Push-Location $PackagePath
|
||||
|
@ -55,13 +103,18 @@ if (Test-Path $PackagePath)
|
|||
|
||||
# TODO: Theoretically we could grab bits from the bin directory instead of having to expand each package, not sure about what we ignore though
|
||||
Expand-MsixUploadPackage $BaselineName -Destination $TempFolder
|
||||
Expand-MsixBundlePackage $BaselineBundleName -Destination $TempFolder2
|
||||
|
||||
# Get all the base file info only (grab stuff in directories but not the directories themselves)
|
||||
$BaselineFiles = Get-ChildItem $TempFolder -Recurse -Attributes !Directory -Exclude "SmokeTest*"
|
||||
$BaselineFiles2 = Get-ChildItem $TempFolder2 -Recurse -Attributes !Directory -Exclude "SmokeTest*"
|
||||
$SmokeTestFiles = Get-ChildItem $TempFolder -Recurse -Attributes !Directory -Include "SmokeTest*"
|
||||
$SmokeTestFiles2 = Get-ChildItem $TempFolder2 -Recurse -Attributes !Directory -Include "SmokeTest*"
|
||||
|
||||
$BaselineFootprint = ($BaselineFiles | Measure-Object -Property Length -sum).Sum + ($SmokeTestFiles | Measure-Object -Property Length -sum).Sum
|
||||
Write-Host ("Baseline Footprint: {0:n0} bytes" -f $BaselineFootprint)
|
||||
$BaselineFootprint2 = ($BaselineFiles2 | Measure-Object -Property Length -sum).Sum + ($SmokeTestFiles2 | Measure-Object -Property Length -sum).Sum
|
||||
Write-Host ("Baseline Max Footprint: {0:n0} bytes" -f $BaselineFootprint)
|
||||
Write-Host ("Baseline Min Footprint: {0:n0} bytes" -f $BaselineFootprint2)
|
||||
Write-Host "-----------------------------------------"
|
||||
|
||||
$PackageList = Get-ChildItem "$PackagePath*.msixupload" -Exclude $BaselineName
|
||||
|
@ -69,21 +122,29 @@ if (Test-Path $PackagePath)
|
|||
#$i = 0
|
||||
foreach ($Package in $PackageList)
|
||||
{
|
||||
# Extract the root package name between the initial '_'
|
||||
$PackageShortName = ($Package.Name -split '_')[1]
|
||||
|
||||
#Write-Progress -Id 0 -Activity "Comparing Against Baseline..." -Status "Prepping Package" -PercentComplete (($i++ / $PackageList.count)*100) -CurrentOperation $Package.Name
|
||||
|
||||
# Make sure we've cleaned-up the last archive
|
||||
Remove-Item $TempFolder -Recurse -Force
|
||||
Remove-Item $TempFolder2 -Recurse -Force
|
||||
|
||||
#$ProgressPreference="SilentlyContinue"
|
||||
Expand-MsixUploadPackage $Package.Name -Destination $TempFolder
|
||||
|
||||
# Also expand the final bundle based on the namespace name within the package.
|
||||
Expand-MsixBundlePackage ($DirPattern -f $PackageShortName) -Destination $TempFolder2
|
||||
#$ProgressPreference="Continue"
|
||||
|
||||
[System.Collections.ArrayList]$PackageFiles = Get-ChildItem $TempFolder -Recurse -Attributes !Directory -Exclude "SmokeTest*"
|
||||
$PackageSmokeTestFiles = Get-ChildItem $TempFolder -Recurse -Attributes !Directory -Include "SmokeTest*"
|
||||
[System.Collections.ArrayList]$PackageFiles2 = Get-ChildItem $TempFolder2 -Recurse -Attributes !Directory -Exclude "SmokeTest*"
|
||||
$PackageSmokeTestFiles2 = Get-ChildItem $TempFolder2 -Recurse -Attributes !Directory -Include "SmokeTest*"
|
||||
|
||||
# TODO: Make function or regex better to extra package name more easily based on a template string at the top or something...
|
||||
$PackageShortName = $Package.Name.substring(10, $Package.Name.Length - 32)
|
||||
Write-Host ("{0} Additional Footprint: {1:n0} bytes" -f $PackageShortName, (($PackageFiles | Measure-Object -Property Length -sum).Sum + ($PackageSmokeTestFiles | Measure-Object -Property Length -sum).Sum - $BaselineFootprint))
|
||||
Write-Host ("{0} Additional Max Footprint: {1:n0} bytes" -f $PackageShortName, (($PackageFiles | Measure-Object -Property Length -sum).Sum + ($PackageSmokeTestFiles | Measure-Object -Property Length -sum).Sum - $BaselineFootprint))
|
||||
Write-Host ("{0} Additional Min Footprint: {1:n0} bytes" -f $PackageShortName, (($PackageFiles2 | Measure-Object -Property Length -sum).Sum + ($PackageSmokeTestFiles2 | Measure-Object -Property Length -sum).Sum - $BaselineFootprint2))
|
||||
|
||||
# Quick check on the base exe file/symbols differences
|
||||
foreach ($file in $SmokeTestFiles)
|
||||
|
@ -132,6 +193,52 @@ if (Test-Path $PackagePath)
|
|||
# TODO: Especially if we add comparison to the main branch, we should format as an actual table and colorize via VT: https://stackoverflow.com/a/49038815/8798708
|
||||
|
||||
#Write-Progress -Id 1 -ParentId 0 -Activity "Comparing Against Baseline..." -Completed
|
||||
Write-Host "-----------------COMPILED----------------"
|
||||
|
||||
# Quick check on the base exe file/symbols differences
|
||||
foreach ($file in $SmokeTestFiles2)
|
||||
{
|
||||
$match = $null
|
||||
$match = $PackageSmokeTestFiles2 | Where-Object {$_.Extension -eq $file.Extension}
|
||||
if ($null -ne $match)
|
||||
{
|
||||
Write-Host (" App Diff: ({0}) = {1:n0}" -f $file.Extension, ($match.Length - $file.Length)) -ForegroundColor DarkCyan
|
||||
}
|
||||
}
|
||||
|
||||
#$j = 0
|
||||
foreach ($file in $BaselineFiles2)
|
||||
{
|
||||
#Write-Progress -Id 1 -ParentId 0 -Activity "Comparing Against Baseline..." -Status "Comparing Package" -PercentComplete (($j++ / $BaselineFiles.count)*100) -CurrentOperation $file.Name
|
||||
|
||||
$match = $null
|
||||
$match = $PackageFiles2 | Where-Object {$_.Name -eq $file.Name}
|
||||
if ($null -ne $match)
|
||||
{
|
||||
# File was in baseline, but has a different size
|
||||
if ($match.Length -ne $file.Length)
|
||||
{
|
||||
Write-Host (" Size Diff: {0} = {1:n0}" -f $file.Name, ($match.Length - $file.Length)) -ForegroundColor Magenta
|
||||
}
|
||||
|
||||
# Remove checked files (performance) and also remaining are new
|
||||
$PackageFiles2.Remove($match)
|
||||
}
|
||||
}
|
||||
|
||||
# List remaining (new) files to this package
|
||||
foreach ($file in $PackageFiles2)
|
||||
{
|
||||
if ($file.Name -match $PackageShortName)
|
||||
{
|
||||
Write-Host (" Lib (self): {0} = {1:n0}" -f $file.Name, $file.Length) -ForegroundColor White
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host (" Additional: {0} = {1:n0}" -f $file.Name, $file.Length) -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "-----------------------------------------"
|
||||
Write-Host
|
||||
}
|
||||
|
@ -140,6 +247,7 @@ if (Test-Path $PackagePath)
|
|||
|
||||
# Clean-up
|
||||
Remove-Item $TempFolder -Recurse -Force
|
||||
Remove-Item $TempFolder2 -Recurse -Force
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#####
|
||||
# This script downloads the specified version of NuGet packages for
|
||||
# the required smoke tests from NuGet.
|
||||
# This is useful for performing current analysis on prior builds of the Toolkit.
|
||||
#
|
||||
# Pass it a Version Number of the Package you'd like to download, otherwise it'll download the LKG.
|
||||
# Defaults download to ../bin/nupkg/ directory
|
||||
####
|
||||
|
||||
param (
|
||||
[string] $Version = '',
|
||||
[string] $DownloadPath = '../bin/nupkg/'
|
||||
)
|
||||
|
||||
$global:ProgressPreference="SilentlyContinue"
|
||||
|
||||
Write-Host "Downloading Project NuGets for Version: $Version"
|
||||
|
||||
$ProjectFilePath = $PSScriptRoot + "\SmokeTests.proj"
|
||||
$NuGetDownloadUrl = "https://www.nuget.org/api/v2/package/{0}/{1}" # PackageName, Version
|
||||
|
||||
[xml]$ProjXml = Get-Content -Path $ProjectFilePath
|
||||
|
||||
$PackageList = $ProjXml.Project.PropertyGroup.ToolkitPackages -split ';'
|
||||
|
||||
Push-Location $DownloadPath
|
||||
|
||||
# Download each package
|
||||
foreach ($Package in $PackageList)
|
||||
{
|
||||
$PackageName = $Package.Trim() # Remove extra whitespace depending on proj file format
|
||||
|
||||
$PackageUrl = ($NuGetDownloadUrl -f $PackageName, $Version)
|
||||
|
||||
Write-Host "Downloading $PackageUrl"
|
||||
|
||||
Invoke-WebRequest $PackageUrl -OutFile ("{0}.{1}.nupkg" -f $PackageName, $Version)
|
||||
}
|
||||
|
||||
Pop-Location
|
||||
|
||||
Write-Host "Done"
|
|
@ -0,0 +1,40 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Toolkit.Uwp.Extensions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace UnitTests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class Test_RectExtensions
|
||||
{
|
||||
[TestCategory("RectExtensions")]
|
||||
[TestMethod]
|
||||
[DataRow(0, 0, 2, 2, 0, 0, 2, 2, true)]// Full intersection.
|
||||
[DataRow(0, 0, 2, 2, 1, 1, 2, 2, true)]// Partial intersection.
|
||||
[DataRow(0, 0, 2, 2, -2, 0, 2, 2, true)]// Left edge intersection.
|
||||
[DataRow(0, 0, 2, 2, 0, -2, 2, 2, true)]// Top edge intersection.
|
||||
[DataRow(0, 0, 2, 2, 2, 0, 2, 2, true)]// Right edge intersection.
|
||||
[DataRow(0, 0, 2, 2, 0, 2, 2, 2, true)]// Bottom edge intersection.
|
||||
[DataRow(0, 0, 2, 2, -2, -2, 2, 2, true)]// Left top corner(0, 0) intersection.
|
||||
[DataRow(0, 0, 2, 2, 2, -2, 2, 2, true)]// Right top corner(2, 0) intersection.
|
||||
[DataRow(0, 0, 2, 2, 2, 2, 2, 2, true)]// Right bottom corner(2, 2) intersection.
|
||||
[DataRow(0, 0, 2, 2, -2, 2, 2, 2, true)]// Left bottom corner(0, 2) intersection.
|
||||
[DataRow(0, 0, 2, 2, 3, 0, 2, 2, false)]// No intersection.
|
||||
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:Parameters should be on same line or separate lines", Justification = "Put the parameters of the same rectangle on the same line is clearer.")]
|
||||
public static void Test_RectExtensions_IntersectsWith(
|
||||
double rect1X, double rect1Y, double rect1Width, double rect1Height,
|
||||
double rect2X, double rect2Y, double rect2Width, double rect2Height,
|
||||
bool shouldIntersectsWith)
|
||||
{
|
||||
var rect1 = new Rect(rect1X, rect1Y, rect1Width, rect1Height);
|
||||
var rect2 = new Rect(rect2X, rect2Y, rect2Width, rect2Height);
|
||||
var isIntersectsWith = rect1.IntersectsWith(rect2);
|
||||
Assert.IsTrue(isIntersectsWith == shouldIntersectsWith);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -133,6 +133,7 @@
|
|||
<Compile Include="Converters\Test_TypeToObjectConverter.cs" />
|
||||
<Compile Include="Extensions\Helpers\ObjectWithNullableBoolProperty.cs" />
|
||||
<Compile Include="Extensions\Test_PointExtensions.cs" />
|
||||
<Compile Include="Extensions\Test_RectExtensions.cs" />
|
||||
<Compile Include="Extensions\Test_SizeExtensions.cs" />
|
||||
<Compile Include="Extensions\Test_BitmapIconExtensionMarkupExtension.cs" />
|
||||
<Compile Include="Extensions\Test_FontIconSourceExtensionMarkupExtension.cs" />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"msbuild-sdks": {
|
||||
"MSBuild.Sdk.Extras": "2.0.54"
|
||||
"MSBuild.Sdk.Extras": "3.0.22"
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче