[Settings]New Landing Page Experimentation (#22365)

Co-authored-by: Sophia Chen <sophia.six.chen@gmail.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Sophia Chen <sophchen@microsoft.com>
This commit is contained in:
Sophia Chen 2023-02-14 18:38:53 -08:00 committed by GitHub
parent 44e28886d7
commit df521b4c9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 1181 additions and 12 deletions

View File

@ -163,6 +163,7 @@ bricelam
BRIGHTGREEN BRIGHTGREEN
Browsable Browsable
bsd bsd
Bson
bstr bstr
bthprops bthprops
bti bti
@ -221,6 +222,7 @@ CLASSNOTAVAILABLE
clickable clickable
clickonce clickonce
CLIENTEDGE CLIENTEDGE
clientid
clientside clientside
CLIPCHILDREN CLIPCHILDREN
Clipperton Clipperton
@ -347,6 +349,7 @@ DARKYELLOW
datareader datareader
datatemplate datatemplate
Datavalue Datavalue
dataversion
DATAW DATAW
davidegiacometti davidegiacometti
Dayof Dayof
@ -1859,6 +1862,7 @@ TRAYMOUSEMESSAGE
triaging triaging
TRK TRK
trl trl
TServer
Tshuapa Tshuapa
TStr TStr
Tuva Tuva
@ -1911,6 +1915,7 @@ unmute
UNORM UNORM
unregistering unregistering
unremapped unremapped
Unstub
unsubscribe unsubscribe
unvirtualized unvirtualized
Updatelayout Updatelayout
@ -1936,6 +1941,7 @@ UYVY
vabdq vabdq
validmodulename validmodulename
Vanara Vanara
variantassignment
vcamp vcamp
vccorlib vccorlib
vcdl vcdl

View File

@ -62,6 +62,9 @@ https?://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
link\.medium\.com/[a-zA-Z0-9]+ link\.medium\.com/[a-zA-Z0-9]+
\bmedium\.com/\@[^/]+/[-\w]+ \bmedium\.com/\@[^/]+/[-\w]+
# experimentation urls
https?://default\.exp-tas\.com/[-_a-zA-Z0-9/]*
publicKeyToken=(['"]|)[0-9a-f]+\g{-1} publicKeyToken=(['"]|)[0-9a-f]+\g{-1}
\@sha256:[0-9a-f]{64}\b \@sha256:[0-9a-f]{64}\b

View File

@ -23,6 +23,7 @@
"PowerToys.Settings.UI.Lib.dll", "PowerToys.Settings.UI.Lib.dll",
"PowerToys.GPOWrapper.dll", "PowerToys.GPOWrapper.dll",
"PowerToys.GPOWrapperProjection.dll", "PowerToys.GPOWrapperProjection.dll",
"PowerToys.AllExperiments.dll",
"modules\\AlwaysOnTop\\PowerToys.AlwaysOnTop.exe", "modules\\AlwaysOnTop\\PowerToys.AlwaysOnTop.exe",
"modules\\AlwaysOnTop\\PowerToys.AlwaysOnTopModuleInterface.dll", "modules\\AlwaysOnTop\\PowerToys.AlwaysOnTopModuleInterface.dll",
@ -202,6 +203,8 @@
"Mono.Cecil.Mdb.dll", "Mono.Cecil.Mdb.dll",
"Mono.Cecil.Pdb.dll", "Mono.Cecil.Pdb.dll",
"Mono.Cecil.Rocks.dll", "Mono.Cecil.Rocks.dll",
"Newtonsoft.Json.dll",
"Newtonsoft.Json.Bson.dll",
"NLog.dll", "NLog.dll",
"HtmlAgilityPack.dll", "HtmlAgilityPack.dll",
"Markdig.Signed.dll", "Markdig.Signed.dll",

View File

@ -69,7 +69,7 @@ steps:
configPath: NuGet.config configPath: NuGet.config
restoreSolution: PowerToys.sln restoreSolution: PowerToys.sln
restoreDirectory: '$(Build.SourcesDirectory)\packages' restoreDirectory: '$(Build.SourcesDirectory)\packages'
- task: VSBuild@1 - task: VSBuild@1
displayName: 'Build PowerToys.sln' displayName: 'Build PowerToys.sln'
inputs: inputs:
@ -227,10 +227,17 @@ steps:
**\powerpreviewTest.dll **\powerpreviewTest.dll
**\UnitTests-FancyZones.dll **\UnitTests-FancyZones.dll
!**\obj\** !**\obj\**
- task: PowerShell@2
displayName: Trigger dotnet welcome message so that it does not cause errors on other scripts
inputs:
targetType: 'inline'
script: |
dotnet list $(build.sourcesdirectory)\src\common\Common.UI\Common.UI.csproj package
- task: PowerShell@2 - task: PowerShell@2
displayName: Verifying Notice.md and Nuget packages match displayName: Verifying Notice.md and Nuget packages match
inputs: inputs:
filePath: '$(build.sourcesdirectory)\.pipelines\verifyNoticeMdAgainstNugetPackages.ps1' filePath: '$(build.sourcesdirectory)\.pipelines\verifyNoticeMdAgainstNugetPackages.ps1'
arguments: -path '$(build.sourcesdirectory)\' arguments: -path '$(build.sourcesdirectory)\'
pwsh: true pwsh: true

View File

@ -23,6 +23,7 @@ parameters:
variables: variables:
IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations
SkipCppCodeAnalysis: 1 # Skip the code analysis to speed up release CI. It runs on PR CI, anyway SkipCppCodeAnalysis: 1 # Skip the code analysis to speed up release CI. It runs on PR CI, anyway
IsExperimentationLive: 1 # The build and installer use this to turn on experimentation
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
resources: resources:

View File

@ -38,6 +38,8 @@ $totalList = $projFiles | ForEach-Object -Parallel {
if($nugetTemp -is [array] -and $nugetTemp.count -gt 3) if($nugetTemp -is [array] -and $nugetTemp.count -gt 3)
{ {
# Need to debug this script? Uncomment this line.
# Write-Host $csproj "`r`n" $nugetTemp "`r`n"
$temp = New-Object System.Collections.ArrayList $temp = New-Object System.Collections.ArrayList
$temp.AddRange($nugetTemp) $temp.AddRange($nugetTemp)
$temp.RemoveRange(0, 3) $temp.RemoveRange(0, 3)

View File

@ -57,4 +57,9 @@
<PackageVersion Include="Vanara.PInvoke.Shell32" Version="3.4.11" /> <PackageVersion Include="Vanara.PInvoke.Shell32" Version="3.4.11" />
<PackageVersion Include="WinUIEx" Version="1.8.0" /> <PackageVersion Include="WinUIEx" Version="1.8.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(IsExperimentationLive)'!=''">
<!-- Additional dependencies used by experimentation -->
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
</ItemGroup>
</Project> </Project>

View File

@ -487,6 +487,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StlThumbnailProviderCpp", "
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SvgThumbnailProviderCpp", "src\modules\previewpane\SvgThumbnailProviderCpp\SvgThumbnailProviderCpp.vcxproj", "{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SvgThumbnailProviderCpp", "src\modules\previewpane\SvgThumbnailProviderCpp\SvgThumbnailProviderCpp.vcxproj", "{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AllExperiments", "src\common\AllExperiments\AllExperiments.csproj", "{9CE59ED5-7087-4353-88EB-788038A73CEC}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64 Debug|ARM64 = Debug|ARM64
@ -2022,6 +2024,18 @@ Global
{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x64.Build.0 = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x64.Build.0 = Release|x64
{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.ActiveCfg = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.ActiveCfg = Release|x64
{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.Build.0 = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.Build.0 = Release|x64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|ARM64.ActiveCfg = Debug|ARM64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|ARM64.Build.0 = Debug|ARM64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x64.ActiveCfg = Debug|x64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x64.Build.0 = Debug|x64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x86.ActiveCfg = Debug|x64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x86.Build.0 = Debug|x64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|ARM64.ActiveCfg = Release|ARM64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|ARM64.Build.0 = Release|ARM64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x64.ActiveCfg = Release|x64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x64.Build.0 = Release|x64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x86.ActiveCfg = Release|x64
{9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x86.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -2192,6 +2206,7 @@ Global
{CA5518ED-0458-4B09-8F53-4122B9888655} = {2F305555-C296-497E-AC20-5FA1B237996A} {CA5518ED-0458-4B09-8F53-4122B9888655} = {2F305555-C296-497E-AC20-5FA1B237996A}
{D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D} = {2F305555-C296-497E-AC20-5FA1B237996A} {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D} = {2F305555-C296-497E-AC20-5FA1B237996A}
{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA} = {2F305555-C296-497E-AC20-5FA1B237996A} {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA} = {2F305555-C296-497E-AC20-5FA1B237996A}
{9CE59ED5-7087-4353-88EB-788038A73CEC} = {1AFB6476-670D-4E80-A464-657E01DFF482}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@ -4,12 +4,15 @@
<?include $(sys.CURRENTDIR)\Common.wxi?> <?include $(sys.CURRENTDIR)\Common.wxi?>
<?define SettingsV2Files=WinUIEx.dll;backup_restore_settings.json;Ijwhost.dll;ColorCode.Core.dll;ColorCode.WinUI.dll;CommunityToolkit.Common.dll;CommunityToolkit.Labs.WinUI.SettingsControls.dll;CommunityToolkit.WinUI.dll;CommunityToolkit.WinUI.UI.Controls.Core.dll;CommunityToolkit.WinUI.UI.Controls.DataGrid.dll;CommunityToolkit.WinUI.UI.Controls.Input.dll;CommunityToolkit.WinUI.UI.Controls.Layout.dll;CommunityToolkit.WinUI.UI.Controls.Markdown.dll;CommunityToolkit.WinUI.UI.Controls.Media.dll;CommunityToolkit.WinUI.UI.Controls.Primitives.dll;CommunityToolkit.WinUI.UI.dll;icon.ico;Microsoft.Graphics.Canvas.Interop.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.Settings.deps.json;PowerToys.Settings.dll;PowerToys.Settings.exe;PowerToys.Settings.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;resources.pri;System.CodeDom.dll;System.IO.Abstractions.dll;WinRT.Runtime.dll;Microsoft.Graphics.Canvas.dll;System.Management.dll;PowerToys.GPOWrapper.dll;System.Text.Json.dll;WindowsBase.dll?> <?define SettingsV2Files=WinUIEx.dll;backup_restore_settings.json;Ijwhost.dll;ColorCode.Core.dll;ColorCode.WinUI.dll;CommunityToolkit.Common.dll;CommunityToolkit.Labs.WinUI.SettingsControls.dll;CommunityToolkit.WinUI.dll;CommunityToolkit.WinUI.UI.Controls.Core.dll;CommunityToolkit.WinUI.UI.Controls.DataGrid.dll;CommunityToolkit.WinUI.UI.Controls.Input.dll;CommunityToolkit.WinUI.UI.Controls.Layout.dll;CommunityToolkit.WinUI.UI.Controls.Markdown.dll;CommunityToolkit.WinUI.UI.Controls.Media.dll;CommunityToolkit.WinUI.UI.Controls.Primitives.dll;CommunityToolkit.WinUI.UI.dll;icon.ico;Microsoft.Graphics.Canvas.Interop.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.Settings.deps.json;PowerToys.Settings.dll;PowerToys.Settings.exe;PowerToys.Settings.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;resources.pri;System.CodeDom.dll;System.IO.Abstractions.dll;WinRT.Runtime.dll;Microsoft.Graphics.Canvas.dll;System.Management.dll;PowerToys.GPOWrapper.dll;System.Text.Json.dll;WindowsBase.dll;PowerToys.AllExperiments.dll?>
<?define SettingsV2AssetsModulesFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;ImageResizer.png;KBM.png;MouseUtils.png;PowerAccent.png;PowerOCR.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ScreenRuler.png;ShortcutGuide.png;VideoConference.png?> <?define SettingsV2AssetsModulesFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;ImageResizer.png;KBM.png;MouseUtils.png;PowerAccent.png;PowerOCR.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ScreenRuler.png;ShortcutGuide.png;VideoConference.png?>
<?define SettingsV2OOBEAssetsModulesFiles=ColorPicker.gif;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;FancyZones.gif;FileExplorer.png;FileLocksmith.gif;ImageResizer.gif;KBM.gif;MouseUtils.gif;PowerAccent.gif;PowerOCR.gif;PowerRename.gif;Run.gif;ScreenRuler.gif;OOBEShortcutGuide.png;VideoConferenceMute.png;OOBEPTHero.png?> <?define SettingsV2OOBEAssetsModulesFiles=ColorPicker.gif;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;FancyZones.gif;FileExplorer.png;FileLocksmith.gif;ImageResizer.gif;KBM.gif;MouseUtils.gif;PowerAccent.gif;PowerOCR.gif;PowerRename.gif;Run.gif;ScreenRuler.gif;OOBEShortcutGuide.png;VideoConferenceMute.png;OOBEPTHero.png?>
<?define SettingsV2OOBEAssetsFluentIconsFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;AlwaysOnTop.png;Awake.png;FileExplorerPreview.png;FindMyMouse.png;Hosts.png;ImageResizer.png;KeyboardManager.png;MouseHighlighter.png;MouseCrosshairs.png;MouseUtils.png;PowerAccent.png;PowerOcr.png;PowerRename.png;PowerToys.png;PowerToysRun.png;ScreenRuler.png;Settings.png;ShortcutGuide.png;VideoConferenceMute.png?> <?define SettingsV2OOBEAssetsFluentIconsFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;AlwaysOnTop.png;Awake.png;FileExplorerPreview.png;FindMyMouse.png;Hosts.png;ImageResizer.png;KeyboardManager.png;MouseHighlighter.png;MouseCrosshairs.png;MouseUtils.png;PowerAccent.png;PowerOcr.png;PowerRename.png;PowerToys.png;PowerToysRun.png;ScreenRuler.png;Settings.png;ShortcutGuide.png;VideoConferenceMute.png?>
<?define SettingsV2MicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?> <?define SettingsV2MicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
<!-- These files are needed for release builds to contain the experimentation DLLs -->
<?define PowerToysExperimentsFiles=Microsoft.VariantAssignment.Client.dll;Microsoft.VariantAssignment.Contract.dll;System.Net.Http.Formatting.dll;Microsoft.Extensions.Configuration.dll;Microsoft.Extensions.Configuration.Abstractions.dll;Microsoft.Extensions.Configuration.Binder.dll;Microsoft.Extensions.DependencyInjection.Abstractions.dll;Microsoft.Extensions.Http.dll;Microsoft.Extensions.Logging.dll;Microsoft.Extensions.Logging.Abstractions.dll;Microsoft.Extensions.Options.dll;Microsoft.Extensions.Primitives.dll;Newtonsoft.Json.dll;Newtonsoft.Json.Bson.dll?>
<Fragment> <Fragment>
<!-- SettingsV2 components --> <!-- SettingsV2 components -->
<DirectoryRef Id="SettingsV2InstallFolder" FileSource="$(var.BinDir)Settings\"> <DirectoryRef Id="SettingsV2InstallFolder" FileSource="$(var.BinDir)Settings\">
@ -18,6 +21,13 @@
<File Id="SV2_$(var.File)" Source="$(var.BinDir)Settings\$(var.File)" /> <File Id="SV2_$(var.File)" Source="$(var.BinDir)Settings\$(var.File)" />
</Component> </Component>
<?endforeach?> <?endforeach?>
<?ifdef env.IsExperimentationLive?>
<?foreach File in $(var.PowerToysExperimentsFiles)?>
<Component Id="SV2CE_$(var.File)" Win64="yes">
<File Id="SV2E_$(var.File)" Source="$(var.BinDir)Settings\$(var.File)" />
</Component>
<?endforeach?>
<?endif?>
</DirectoryRef> </DirectoryRef>
<DirectoryRef Id="SettingsV2AssetsInstallFolder" FileSource="$(var.BinDir)Settings\Assets"> <DirectoryRef Id="SettingsV2AssetsInstallFolder" FileSource="$(var.BinDir)Settings\Assets">
<Component Id="SettingsV2Assets_LogoScale200" Win64="yes"> <Component Id="SettingsV2Assets_LogoScale200" Win64="yes">
@ -78,6 +88,11 @@
<?foreach File in $(var.SettingsV2Files)?> <?foreach File in $(var.SettingsV2Files)?>
<ComponentRef Id="SV2C_$(var.File)" /> <ComponentRef Id="SV2C_$(var.File)" />
<?endforeach?> <?endforeach?>
<?ifdef env.IsExperimentationLive?>
<?foreach File in $(var.PowerToysExperimentsFiles)?>
<ComponentRef Id="SV2CE_$(var.File)" />
<?endforeach?>
<?endif?>
<ComponentRef Id="SettingsV2Assets_LogoScale200" /> <ComponentRef Id="SettingsV2Assets_LogoScale200" />
<ComponentRef Id="SettingsV2Assets_SplashScreen" /> <ComponentRef Id="SettingsV2Assets_SplashScreen" />
<ComponentRef Id="SettingsV2Assets_StoreLogo_Scale100" /> <ComponentRef Id="SettingsV2Assets_StoreLogo_Scale100" />

View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Version.props" />
<PropertyGroup>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TargetName>PowerToys.AllExperiments</TargetName>
<MockDirectory>.\Microsoft.VariantAssignment\</MockDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
</ItemGroup>
<!-- Experimentation is live, forcing inclusion -->
<ItemGroup Condition="'$(IsExperimentationLive)'!=''">
<!-- Newtonsoft.Json is included and a version specified in Directory.Packages.props to avoid a vulnerability from older versions. -->
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Microsoft.VariantAssignment.Client" />
<PackageReference Include="Microsoft.VariantAssignment.Contract" />
<Compile Remove=".\$(MockDirectory)\Client\*.cs" />
<Compile Remove=".\$(MockDirectory)\Contract\*.cs" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,211 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
using Microsoft.PowerToys.Telemetry;
using Microsoft.VariantAssignment.Client;
using Microsoft.VariantAssignment.Contract;
using Windows.System.Profile;
namespace AllExperiments
{
// The dependencies required to build this project are only available in the official build pipeline and are internal to Microsoft.
// However, this project is not required to build a test version of the application.
public class Experiments
{
public enum ExperimentState
{
Enabled,
Disabled,
NotLoaded,
}
#pragma warning disable SA1401 // Need to use LandingPageExperiment as a static property in OobeShellPage.xaml.cs
public static ExperimentState LandingPageExperiment = ExperimentState.NotLoaded;
#pragma warning restore SA1401
public async Task<bool> EnableLandingPageExperimentAsync()
{
if (Experiments.LandingPageExperiment != ExperimentState.NotLoaded)
{
return Experiments.LandingPageExperiment == ExperimentState.Enabled;
}
Experiments varServ = new Experiments();
await varServ.VariantAssignmentProvider_Initialize();
var landingPageExperiment = varServ.IsExperiment;
Experiments.LandingPageExperiment = landingPageExperiment ? ExperimentState.Enabled : ExperimentState.Disabled;
return landingPageExperiment;
}
private async Task VariantAssignmentProvider_Initialize()
{
IsExperiment = false;
string jsonFilePath = CreateFilePath();
var vaSettings = new VariantAssignmentClientSettings
{
Endpoint = new Uri("https://default.exp-tas.com/exptas77/a7a397e7-6fbe-4f21-a4e9-3f542e4b000e-exppowertoys/api/v1/tas"),
EnableCaching = true,
ResponseCacheTime = TimeSpan.FromMinutes(5),
};
try
{
var vaClient = vaSettings.GetTreatmentAssignmentServiceClient();
var vaRequest = GetVariantAssignmentRequest();
using var variantAssignments = await vaClient.GetVariantAssignmentsAsync(vaRequest).ConfigureAwait(false);
if (variantAssignments.AssignedVariants.Count != 0)
{
var dataVersion = variantAssignments.DataVersion;
var featureVariables = variantAssignments.GetFeatureVariables();
var assignmentContext = variantAssignments.GetAssignmentContext();
var featureFlagValue = featureVariables[0].GetStringValue();
var experimentGroup = string.Empty;
string json = File.ReadAllText(jsonFilePath);
var jsonDictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
if (jsonDictionary != null)
{
if (!jsonDictionary.ContainsKey("dataversion"))
{
jsonDictionary.Add("dataversion", dataVersion);
}
if (!jsonDictionary.ContainsKey("variantassignment"))
{
jsonDictionary.Add("variantassignment", featureFlagValue);
}
else
{
var jsonDataVersion = jsonDictionary["dataversion"].ToString();
if (jsonDataVersion != null && int.Parse(jsonDataVersion) < dataVersion)
{
jsonDictionary["dataversion"] = dataVersion;
jsonDictionary["variantassignment"] = featureFlagValue;
}
}
experimentGroup = jsonDictionary["variantassignment"].ToString();
string output = JsonSerializer.Serialize(jsonDictionary);
File.WriteAllText(jsonFilePath, output);
}
if (experimentGroup == "alternate" && AssignmentUnit != string.Empty)
{
IsExperiment = true;
}
PowerToysTelemetry.Log.WriteEvent(new OobeVariantAssignmentEvent() { AssignmentContext = assignmentContext, ClientID = AssignmentUnit });
}
}
catch (HttpRequestException ex)
{
string json = File.ReadAllText(jsonFilePath);
var jsonDictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
if (jsonDictionary != null)
{
if (jsonDictionary.ContainsKey("variantassignment"))
{
if (jsonDictionary["variantassignment"].ToString() == "alternate" && AssignmentUnit != string.Empty)
{
IsExperiment = true;
}
}
else
{
jsonDictionary["variantassignment"] = "current";
}
}
string output = JsonSerializer.Serialize(jsonDictionary);
File.WriteAllText(jsonFilePath, output);
Logger.LogError("Error getting to TAS endpoint", ex);
}
catch (Exception ex)
{
Logger.LogError("Error getting variant assignments for experiment", ex);
}
}
public bool IsExperiment { get; set; }
private string? AssignmentUnit { get; set; }
private IVariantAssignmentRequest GetVariantAssignmentRequest()
{
var jsonFilePath = CreateFilePath();
try
{
if (!File.Exists(jsonFilePath))
{
AssignmentUnit = Guid.NewGuid().ToString();
var data = new Dictionary<string, string>()
{
["clientid"] = AssignmentUnit,
};
string jsonData = JsonSerializer.Serialize(data);
File.WriteAllText(jsonFilePath, jsonData);
}
else
{
string json = File.ReadAllText(jsonFilePath);
var jsonDictionary = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(json);
if (jsonDictionary != null)
{
AssignmentUnit = jsonDictionary["clientid"]?.ToString();
}
}
}
catch (Exception ex)
{
Logger.LogError("Error creating/getting AssignmentUnit", ex);
}
var attrNames = new List<string> { "FlightRing", "c:InstallLanguage" };
var attrData = AnalyticsInfo.GetSystemPropertiesAsync(attrNames).AsTask().GetAwaiter().GetResult();
var flightRing = string.Empty;
var installLanguage = string.Empty;
if (attrData.ContainsKey("FlightRing"))
{
flightRing = attrData["FlightRing"];
}
if (attrData.ContainsKey("InstallLanguage"))
{
installLanguage = attrData["InstallLanguage"];
}
return new VariantAssignmentRequest
{
Parameters =
{
{ "installLanguage", installLanguage },
{ "flightRing", flightRing },
{ "clientid", AssignmentUnit },
},
};
}
private string CreateFilePath()
{
var exeDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var settingsPath = @"Microsoft\PowerToys\experimentation.json";
var filePath = Path.Combine(exeDir, settingsPath);
return filePath;
}
}
}

View File

@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO.Abstractions;
namespace AllExperiments
{
public static class Logger
{
private static readonly IFileSystem FileSystem = new FileSystem();
private static readonly IPath Path = FileSystem.Path;
private static readonly IDirectory Directory = FileSystem.Directory;
private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs\\Experimentation");
static Logger()
{
if (!Directory.Exists(ApplicationLogPath))
{
Directory.CreateDirectory(ApplicationLogPath);
}
// Using InvariantCulture since this is used for a log file name
var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
}
public static void LogInfo(string message)
{
Log(message, "INFO");
}
public static void LogError(string message)
{
Log(message, "ERROR");
#if DEBUG
Debugger.Break();
#endif
}
public static void LogError(string message, Exception e)
{
Log(
message + Environment.NewLine +
e?.Message + Environment.NewLine +
"Inner exception: " + Environment.NewLine +
e?.InnerException?.Message + Environment.NewLine +
"Stack trace: " + Environment.NewLine +
e?.StackTrace,
"ERROR");
#if DEBUG
Debugger.Break();
#endif
}
private static void Log(string message, string type)
{
Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay);
Trace.Indent();
Trace.WriteLine(GetCallerInfo());
Trace.WriteLine(message);
Trace.Unindent();
}
private static string GetCallerInfo()
{
StackTrace stackTrace = new StackTrace();
var methodName = stackTrace.GetFrame(3)?.GetMethod();
var className = methodName?.DeclaringType?.Name;
return "[Method]: " + methodName?.Name + " [Class]: " + className;
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.VariantAssignment.Contract;
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Client
{
#pragma warning disable SA1200 // Using directives should be placed correctly
using TreatmentAssignmentServiceClient = VariantAssignmentServiceClient<TreatmentAssignmentServiceResponse>;
#pragma warning restore SA1200 // Using directives should be placed correctly
public static class VariantAssignmentClientExtensionMethods
{
public static IVariantAssignmentProvider GetTreatmentAssignmentServiceClient(this VariantAssignmentClientSettings settings)
{
return new TreatmentAssignmentServiceClient();
}
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.VariantAssignment.Contract;
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Client
{
internal partial class VariantAssignmentServiceClient<TServerResponse> : IVariantAssignmentProvider, IDisposable
where TServerResponse : VariantAssignmentServiceResponse
{
public void Dispose()
{
throw new NotImplementedException();
}
public Task<IVariantAssignmentResponse> GetVariantAssignmentsAsync(IVariantAssignmentRequest request, CancellationToken ct = default)
{
return Task.FromResult(EmptyVariantAssignmentResponse.Instance);
}
}
}

View File

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public class EmptyVariantAssignmentResponse : IVariantAssignmentResponse
{
/// <summary>
/// Singleton instance of <see cref="EmptyVariantAssignmentResponse"/>.
/// </summary>
public static readonly IVariantAssignmentResponse Instance = new EmptyVariantAssignmentResponse();
public EmptyVariantAssignmentResponse()
{
}
public long DataVersion => 0;
public string Thumbprint => string.Empty;
/// <inheritdoc/>
public IReadOnlyCollection<IAssignedVariant> AssignedVariants => Array.Empty<IAssignedVariant>();
/// <inheritdoc/>
#pragma warning disable CS8603 // Possible null reference return.
public IFeatureVariable GetFeatureVariable(IReadOnlyList<string> path) => null;
#pragma warning restore CS8603 // Possible null reference return.
/// <inheritdoc/>
public IReadOnlyList<IFeatureVariable> GetFeatureVariables(IReadOnlyList<string> prefix) => Array.Empty<IFeatureVariable>();
void IDisposable.Dispose()
{
}
string IVariantAssignmentResponse.GetAssignmentContext()
{
throw new NotImplementedException();
}
IReadOnlyList<IFeatureVariable> IVariantAssignmentResponse.GetFeatureVariables()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public interface IAssignedVariant
{
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public interface IFeatureVariable
{
/// <summary>
/// Gets the variable's value as a string.
/// </summary>
/// <returns>String value of the variable.</returns>
string GetStringValue();
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public interface IVariantAssignmentProvider : IDisposable
{
/// <summary>
/// Computes variant assignments based on <paramref name="request"/> data.
/// </summary>
/// <param name="request">Variant assignment parameters.</param>
/// <param name="ct">Propagates notification that operations should be canceled.</param>
/// <returns>An awaitable task that returns a <see cref="IVariantAssignmentResponse"/>.</returns>
Task<IVariantAssignmentResponse> GetVariantAssignmentsAsync(IVariantAssignmentRequest request, CancellationToken ct = default);
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public interface IVariantAssignmentRequest
{
/// <summary>
/// Gets inputs used for evaluating filters, assignment units, etc.
/// </summary>
IReadOnlyCollection<(string Key, string Value)> Parameters { get; }
}
}

View File

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
/// <summary>
/// Snapshot of variant assignments.
/// </summary>
public interface IVariantAssignmentResponse : IDisposable
{
///// <summary>
///// Gets the serial number of variant assignment configuration snapshot used when assigning variants.
///// </summary>
long DataVersion { get; }
///// <summary>
///// Get a hash of the response suitable for caching.
///// </summary>
// string Thumbprint { get; }
/// <summary>
/// Gets the variants assigned based on request parameters and a variant configuration snapshot.
/// </summary>
IReadOnlyCollection<IAssignedVariant> AssignedVariants { get; }
/// <summary>
/// Gets feature variables assigned by variants in this response.
/// </summary>
/// <param name="prefix">(Optional) Filter feature variables where <see cref="IFeatureVariable.KeySegments"/> contains the <paramref name="prefix"/>.</param>
/// <returns>Range of matching feature variables.</returns>
IReadOnlyList<IFeatureVariable> GetFeatureVariables(IReadOnlyList<string> prefix);
// this actually part of the interface but gets the job done
IReadOnlyList<IFeatureVariable> GetFeatureVariables();
// this actually part of the interface but gets the job done
string GetAssignmentContext();
/// <summary>
/// Gets a single feature variable assigned by variants in this response.
/// </summary>
/// <param name="path">Exact feature variable path.</param>
/// <returns>Matching feature variable or null.</returns>
IFeatureVariable GetFeatureVariable(IReadOnlyList<string> path);
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
internal class TreatmentAssignmentServiceResponse : VariantAssignmentServiceResponse
{
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.ComponentModel.DataAnnotations;
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
/// <summary>
/// Configuration for variant assignment service client.
/// </summary>
public class VariantAssignmentClientSettings
{
/// <summary>
/// Gets or sets the variant assignment service endpoint URL.
/// </summary>
[Required]
public Uri? Endpoint { get; set; }
/// <summary>
/// Gets or sets a value indicating whether gets or sets a value whether client side request caching should be enabled.
/// </summary>
public bool EnableCaching { get; set; }
/// <summary>
/// Gets or sets the the maximum time a cached variant assignment response may be used without re-validating.
/// </summary>
public TimeSpan ResponseCacheTime { get; set; }
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Specialized;
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public class VariantAssignmentRequest : IVariantAssignmentRequest
{
private NameValueCollection _parameters = new NameValueCollection();
/// <summary>
/// Gets or sets mutable <see cref="IVariantAssignmentRequest.Parameters"/>.
/// </summary>
public NameValueCollection Parameters { get => _parameters; set => _parameters = value; }
IReadOnlyCollection<(string Key, string Value)> IVariantAssignmentRequest.Parameters => (IReadOnlyCollection<(string Key, string Value)>)_parameters;
}
}

View File

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
/// <summary>
/// Mutable implementation of <see cref="IVariantAssignmentResponse"/> for (de)serialization.
/// </summary>
internal class VariantAssignmentServiceResponse : IVariantAssignmentResponse, IDisposable
{
/// <inheritdoc />
public virtual long DataVersion { get; set; }
public virtual IReadOnlyCollection<IAssignedVariant> AssignedVariants { get; set; } = Array.Empty<IAssignedVariant>();
public IFeatureVariable GetFeatureVariable(IReadOnlyList<string> path)
{
throw new NotImplementedException();
}
public IReadOnlyList<IFeatureVariable> GetFeatureVariables(IReadOnlyList<string> prefix)
{
throw new NotImplementedException();
}
protected virtual void Dispose(bool disposing)
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IReadOnlyList<IFeatureVariable> GetFeatureVariables()
{
throw new NotImplementedException();
}
public string GetAssignmentContext()
{
return string.Empty;
}
}
}

View File

@ -16,6 +16,7 @@
static std::wstring settings_theme = L"system"; static std::wstring settings_theme = L"system";
static bool run_as_elevated = false; static bool run_as_elevated = false;
static bool download_updates_automatically = true; static bool download_updates_automatically = true;
static bool enable_experimentation = true;
json::JsonObject GeneralSettings::to_json() json::JsonObject GeneralSettings::to_json()
{ {
@ -37,6 +38,7 @@ json::JsonObject GeneralSettings::to_json()
result.SetNamedValue(L"is_elevated", json::value(isElevated)); result.SetNamedValue(L"is_elevated", json::value(isElevated));
result.SetNamedValue(L"run_elevated", json::value(isRunElevated)); result.SetNamedValue(L"run_elevated", json::value(isRunElevated));
result.SetNamedValue(L"download_updates_automatically", json::value(downloadUpdatesAutomatically)); result.SetNamedValue(L"download_updates_automatically", json::value(downloadUpdatesAutomatically));
result.SetNamedValue(L"enable_experimentation", json::value(enableExperimentation));
result.SetNamedValue(L"is_admin", json::value(isAdmin)); result.SetNamedValue(L"is_admin", json::value(isAdmin));
result.SetNamedValue(L"theme", json::value(theme)); result.SetNamedValue(L"theme", json::value(theme));
result.SetNamedValue(L"system_theme", json::value(systemTheme)); result.SetNamedValue(L"system_theme", json::value(systemTheme));
@ -55,6 +57,7 @@ json::JsonObject load_general_settings()
} }
run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false); run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false);
download_updates_automatically = loaded.GetNamedBoolean(L"download_updates_automatically", true) && check_user_is_admin(); download_updates_automatically = loaded.GetNamedBoolean(L"download_updates_automatically", true) && check_user_is_admin();
enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation",true);
return loaded; return loaded;
} }
@ -67,6 +70,7 @@ GeneralSettings get_general_settings()
.isRunElevated = run_as_elevated, .isRunElevated = run_as_elevated,
.isAdmin = is_user_admin, .isAdmin = is_user_admin,
.downloadUpdatesAutomatically = download_updates_automatically && is_user_admin, .downloadUpdatesAutomatically = download_updates_automatically && is_user_admin,
.enableExperimentation = enable_experimentation,
.theme = settings_theme, .theme = settings_theme,
.systemTheme = WindowsColors::is_dark_mode() ? L"dark" : L"light", .systemTheme = WindowsColors::is_dark_mode() ? L"dark" : L"light",
.powerToysVersion = get_product_version() .powerToysVersion = get_product_version()
@ -89,6 +93,8 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save)
download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true); download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true);
enable_experimentation = general_configs.GetNamedBoolean(L"enable_experimentation", true);
if (json::has(general_configs, L"startup", json::JsonValueType::Boolean)) if (json::has(general_configs, L"startup", json::JsonValueType::Boolean))
{ {
const bool startup = general_configs.GetNamedBoolean(L"startup"); const bool startup = general_configs.GetNamedBoolean(L"startup");

View File

@ -11,6 +11,7 @@ struct GeneralSettings
bool isRunElevated; bool isRunElevated;
bool isAdmin; bool isAdmin;
bool downloadUpdatesAutomatically; bool downloadUpdatesAutomatically;
bool enableExperimentation;
std::wstring theme; std::wstring theme;
std::wstring systemTheme; std::wstring systemTheme;
std::wstring powerToysVersion; std::wstring powerToysVersion;

View File

@ -56,6 +56,7 @@ void Trace::SettingsChanged(const GeneralSettings& settings)
TraceLoggingWideString(enabledModules.c_str(), "ModulesEnabled"), TraceLoggingWideString(enabledModules.c_str(), "ModulesEnabled"),
TraceLoggingBoolean(settings.isRunElevated, "AlwaysRunElevated"), TraceLoggingBoolean(settings.isRunElevated, "AlwaysRunElevated"),
TraceLoggingBoolean(settings.downloadUpdatesAutomatically, "DownloadUpdatesAutomatically"), TraceLoggingBoolean(settings.downloadUpdatesAutomatically, "DownloadUpdatesAutomatically"),
TraceLoggingBoolean(settings.enableExperimentation, "EnableExperimentation"),
TraceLoggingWideString(settings.theme.c_str(), "Theme"), TraceLoggingWideString(settings.theme.c_str(), "Theme"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),

View File

@ -49,12 +49,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("download_updates_automatically")] [JsonPropertyName("download_updates_automatically")]
public bool AutoDownloadUpdates { get; set; } public bool AutoDownloadUpdates { get; set; }
[JsonPropertyName("enable_experimentation")]
public bool EnableExperimentation { get; set; }
public GeneralSettings() public GeneralSettings()
{ {
Startup = false; Startup = false;
IsAdmin = false; IsAdmin = false;
IsElevated = false; IsElevated = false;
AutoDownloadUpdates = false; AutoDownloadUpdates = false;
EnableExperimentation = true;
Theme = "system"; Theme = "system";
SystemTheme = "light"; SystemTheme = "light";
try try

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events
{
[EventData]
public class OobeVariantAssignmentEvent : EventBase, IEvent
{
public string AssignmentContext { get; set; }
public string ClientID { get; set; }
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework> <TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers>
<Version>$(Version).0</Version> <Version>$(Version).0</Version>
</PropertyGroup> </PropertyGroup>

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

View File

@ -10,12 +10,13 @@
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="280" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image <Image
x:Name="HeaderImage" x:Name="HeaderImage"
Height="{x:Bind HeroImageHeight}"
Source="{x:Bind HeroImage}" Source="{x:Bind HeroImage}"
Stretch="UniformToFill" /> Stretch="UniformToFill" />
@ -23,9 +24,7 @@
Grid.Row="1" Grid.Row="1"
Padding="32,24,32,24" Padding="32,24,32,24"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<StackPanel <StackPanel VerticalAlignment="Top" Orientation="Vertical">
VerticalAlignment="Top"
Orientation="Vertical">
<TextBlock <TextBlock
x:Name="TitleTxt" x:Name="TitleTxt"
@ -49,4 +48,4 @@
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -32,6 +32,12 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
set => SetValue(HeroImageProperty, value); set => SetValue(HeroImageProperty, value);
} }
public double HeroImageHeight
{
get { return (double)GetValue(HeroImageHeightProperty); }
set { SetValue(HeroImageHeightProperty, value); }
}
public object PageContent public object PageContent
{ {
get { return (object)GetValue(PageContentProperty); } get { return (object)GetValue(PageContentProperty); }
@ -42,5 +48,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(SettingsPageControl), new PropertyMetadata(default(string))); public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(SettingsPageControl), new PropertyMetadata(default(string)));
public static readonly DependencyProperty HeroImageProperty = DependencyProperty.Register("HeroImage", typeof(string), typeof(SettingsPageControl), new PropertyMetadata(default(string))); public static readonly DependencyProperty HeroImageProperty = DependencyProperty.Register("HeroImage", typeof(string), typeof(SettingsPageControl), new PropertyMetadata(default(string)));
public static readonly DependencyProperty PageContentProperty = DependencyProperty.Register("PageContent", typeof(object), typeof(SettingsPageControl), new PropertyMetadata(new Grid())); public static readonly DependencyProperty PageContentProperty = DependencyProperty.Register("PageContent", typeof(object), typeof(SettingsPageControl), new PropertyMetadata(new Grid()));
public static readonly DependencyProperty HeroImageHeightProperty = DependencyProperty.Register("HeroImageHeight", typeof(double), typeof(SettingsPageControl), new PropertyMetadata(280.0));
} }
} }

View File

@ -0,0 +1,193 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeOverviewAlternate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<controls:OOBEPageControl
x:Uid="Oobe_Overview"
HeroImage="ms-appx:///Assets/Modules/OOBE/OOBEPTHero.png"
HeroImageHeight="120">
<controls:OOBEPageControl.PageContent>
<StackPanel Orientation="Vertical">
<TextBlock
x:Uid="Alternate_OOBE_Description"
Margin="0,24,0,12"
FontWeight="SemiBold" />
<GridView Margin="-8,0,0,0" SelectionMode="None">
<GridViewItem>
<Grid
Width="280"
Margin="8"
Padding="16,16,16,10"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Width="36"
HorizontalAlignment="Left"
Source="ms-appx:///Assets/FluentIcons/FluentIconsFancyZones.png" />
<TextBlock
x:Uid="Alternate_OOBE_FancyZones_Title"
Grid.Row="1"
Margin="0,12,0,6"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
TextWrapping="Wrap" />
<TextBlock
x:Uid="Alternate_OOBE_FancyZones_Description"
Grid.Row="2"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
<controls:ShortcutWithTextLabelControl
x:Name="FancyZonesHotkeyControl"
Grid.Row="3"
Margin="0,8,0,0" />
</Grid>
</GridViewItem>
<GridViewItem>
<Grid
Width="280"
Padding="16,16,16,10"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Width="36"
HorizontalAlignment="Left"
Source="ms-appx:///Assets/FluentIcons/FluentIconsPowerToysRun.png" />
<TextBlock
x:Uid="Alternate_OOBE_Run_Title"
Grid.Row="1"
Margin="0,12,0,6"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
TextWrapping="Wrap" />
<TextBlock
x:Uid="Alternate_OOBE_Run_Description"
Grid.Row="2"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
<controls:ShortcutWithTextLabelControl
x:Name="RunHotkeyControl"
Grid.Row="3"
Margin="0,8,0,0" />
</Grid>
</GridViewItem>
<GridViewItem>
<Grid
Width="280"
Padding="16,16,16,10"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Width="36"
HorizontalAlignment="Left"
Source="ms-appx:///Assets/FluentIcons/FluentIconsColorPicker.png" />
<TextBlock
x:Uid="Alternate_OOBE_ColorPicker_Title"
Grid.Row="1"
Margin="0,12,0,6"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
TextWrapping="Wrap" />
<TextBlock
x:Uid="Alternate_OOBE_ColorPicker_Description"
Grid.Row="2"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="To pick a color:"
TextWrapping="Wrap" />
<controls:ShortcutWithTextLabelControl
x:Name="ColorPickerHotkeyControl"
Grid.Row="3"
Margin="0,8,0,0" />
</Grid>
</GridViewItem>
<GridViewItem>
<Grid
Width="280"
Padding="16,16,16,10"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Width="36"
HorizontalAlignment="Left"
Source="ms-appx:///Assets/FluentIcons/FluentIconsAlwaysOnTop.png" />
<TextBlock
x:Uid="Alternate_OOBE_AlwaysOnTop_Title"
Grid.Row="1"
Margin="0,12,0,6"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
TextWrapping="Wrap" />
<TextBlock
x:Uid="Alternate_OOBE_AlwaysOnTop_Description"
Grid.Row="2"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
<controls:ShortcutWithTextLabelControl
x:Name="AlwaysOnTopHotkeyControl"
Grid.Row="3"
Margin="0,8,0,0" />
</Grid>
</GridViewItem>
</GridView>
</StackPanel>
</controls:OOBEPageControl.PageContent>
</controls:OOBEPageControl>
</Page>

View File

@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.Views;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
public sealed partial class OobeOverviewAlternate : Page
{
public OobePowerToysModule ViewModel { get; set; }
public OobeOverviewAlternate()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]);
DataContext = ViewModel;
FancyZonesHotkeyControl.Keys = SettingsRepository<FancyZonesSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.GetKeysList();
RunHotkeyControl.Keys = SettingsRepository<PowerLauncherSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.OpenPowerLauncher.GetKeysList();
ColorPickerHotkeyControl.Keys = SettingsRepository<ColorPickerSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.GetKeysList();
AlwaysOnTopHotkeyControl.Keys = SettingsRepository<AlwaysOnTopSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.Hotkey.Value.GetKeysList();
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(GeneralPage));
}
ViewModel.LogOpeningSettingsEvent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModel.LogOpeningModuleEvent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModel.LogClosingModuleEvent();
}
}
}

View File

@ -0,0 +1,23 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<!-- Licensed under the MIT License. See LICENSE in the project root for license information. -->
<Page
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeOverviewPlaceholder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Loaded="Page_Loaded"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Margin="0,24,0,0">
<ProgressRing
x:Name="LoadingProgressRing"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsIndeterminate="True"
Visibility="Visible" />
</Grid>
</Page>

View File

@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Threading.Tasks;
using AllExperiments;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.Services;
using Microsoft.PowerToys.Settings.UI.Views;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
public sealed partial class OobeOverviewPlaceholder : Page
{
public OobePowerToysModule ViewModel { get; set; }
public OobeOverviewPlaceholder()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]);
DataContext = ViewModel;
}
private static async Task<bool> GetIsExperiment()
{
Experiments landingPageExp = new Experiments();
var experimentEnabled = await landingPageExp.EnableLandingPageExperimentAsync();
return experimentEnabled;
}
private async void Reload()
{
var isExperiment = await GetIsExperiment();
if (isExperiment)
{
this.Frame.Navigate(typeof(OobeOverviewAlternate));
}
else
{
this.Frame.Navigate(typeof(OobeOverview));
}
}
private void Page_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
Reload();
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(GeneralPage));
}
ViewModel.LogOpeningSettingsEvent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModel.LogOpeningModuleEvent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModel.LogClosingModuleEvent();
}
}
}

View File

@ -4,6 +4,8 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Globalization;
using AllExperiments;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
@ -47,10 +49,16 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public ObservableCollection<OobePowerToysModule> Modules { get; } public ObservableCollection<OobePowerToysModule> Modules { get; }
private static ISettingsUtils settingsUtils = new SettingsUtils();
private bool ExperimentationToggleSwitchEnabled { get; set; } = true;
public OobeShellPage() public OobeShellPage()
{ {
InitializeComponent(); InitializeComponent();
ExperimentationToggleSwitchEnabled = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils).SettingsConfig.EnableExperimentation;
DataContext = ViewModel; DataContext = ViewModel;
OobeShellHandler = this; OobeShellHandler = this;
UpdateUITheme(); UpdateUITheme();
@ -186,7 +194,27 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{ {
switch (selectedItem.Tag) switch (selectedItem.Tag)
{ {
case "Overview": NavigationFrame.Navigate(typeof(OobeOverview)); break; case "Overview":
if (ExperimentationToggleSwitchEnabled)
{
switch (AllExperiments.Experiments.LandingPageExperiment)
{
case Experiments.ExperimentState.Enabled:
NavigationFrame.Navigate(typeof(OobeOverviewAlternate)); break;
case Experiments.ExperimentState.Disabled:
NavigationFrame.Navigate(typeof(OobeOverview)); break;
case Experiments.ExperimentState.NotLoaded:
NavigationFrame.Navigate(typeof(OobeOverviewPlaceholder)); break;
}
break;
}
else
{
NavigationFrame.Navigate(typeof(OobeOverview));
break;
}
case "WhatsNew": NavigationFrame.Navigate(typeof(OobeWhatsNew)); break; case "WhatsNew": NavigationFrame.Navigate(typeof(OobeWhatsNew)); break;
case "AlwaysOnTop": NavigationFrame.Navigate(typeof(OobeAlwaysOnTop)); break; case "AlwaysOnTop": NavigationFrame.Navigate(typeof(OobeAlwaysOnTop)); break;
case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break; case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break;

View File

@ -55,6 +55,9 @@
<PropertyGroup> <PropertyGroup>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json</RestoreAdditionalProjectSources> <RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json</RestoreAdditionalProjectSources>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="OOBE\Views\OobeOverviewPlaceholder.xaml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Controls\ColorFormatEditor.xaml" /> <None Remove="Controls\ColorFormatEditor.xaml" />
</ItemGroup> </ItemGroup>
@ -91,15 +94,19 @@
<!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging <!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
Tools extension to be activated for this project even if the Windows App SDK Nuget Tools extension to be activated for this project even if the Windows App SDK Nuget
package has not yet been restored --> package has not yet been restored -->
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'"> <ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
<ProjectCapability Include="Msix" /> <ProjectCapability Include="Msix" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\common\AllExperiments\AllExperiments.csproj" />
<ProjectReference Include="..\..\common\GPOWrapper\GPOWrapper.vcxproj" /> <ProjectReference Include="..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" /> <ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" /> <ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" /> <ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="FlyoutWindow.xaml"> <Page Update="FlyoutWindow.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@ -122,10 +129,17 @@
<None Update="icon.ico"> <None Update="icon.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<Page Update="OOBE\Views\OobeOverviewPlaceholder.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="OOBE\Views\OobeHosts.xaml"> <Page Update="OOBE\Views\OobeHosts.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Update="OOBE\Views\OobeOverviewAlternate.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="OOBE\Views\OobePowerOCR.xaml"> <Page Update="OOBE\Views\OobePowerOCR.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime> <XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>

View File

@ -2830,6 +2830,10 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Launch Host File Editor</value> <value>Launch Host File Editor</value>
<comment>"Host File Editor" is a product name</comment> <comment>"Host File Editor" is a product name</comment>
</data> </data>
<data name="Hosts_LaunchButton_Accessible.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Launch Host File Editor</value>
<comment>"Host File Editor" is a product name</comment>
</data>
<data name="Hosts_AdditionalLinesPosition.Header" xml:space="preserve"> <data name="Hosts_AdditionalLinesPosition.Header" xml:space="preserve">
<value>Position of additional content</value> <value>Position of additional content</value>
</data> </data>
@ -2891,6 +2895,39 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="TextExtractor_Languages.Header" xml:space="preserve"> <data name="TextExtractor_Languages.Header" xml:space="preserve">
<value>Preferred language</value> <value>Preferred language</value>
</data> </data>
<data name="Alternate_OOBE_AlwaysOnTop_Description.Text" xml:space="preserve">
<value>Pin a window so that:</value>
</data>
<data name="Alternate_OOBE_AlwaysOnTop_Title.Text" xml:space="preserve">
<value>Always On Top</value>
</data>
<data name="Alternate_OOBE_ColorPicker_Description.Text" xml:space="preserve">
<value>To pick a color:</value>
</data>
<data name="Alternate_OOBE_ColorPicker_Title.Text" xml:space="preserve">
<value>Color Picker</value>
</data>
<data name="Alternate_OOBE_Description.Text" xml:space="preserve">
<value>Here are a few shortcuts to get you started:</value>
</data>
<data name="Alternate_OOBE_FancyZones_Description.Text" xml:space="preserve">
<value>To open the FancyZones editor, press:</value>
</data>
<data name="Alternate_OOBE_FancyZones_Title.Text" xml:space="preserve">
<value>FancyZones</value>
</data>
<data name="Alternate_OOBE_Run_Description.Text" xml:space="preserve">
<value>Get access to your files and more:</value>
</data>
<data name="Alternate_OOBE_Run_Title.Text" xml:space="preserve">
<value>PowerToys Run</value>
</data>
<data name="GeneralPage_EnableExperimentation.Description" xml:space="preserve">
<value>Only affects Windows Insider builds</value>
</data>
<data name="GeneralPage_EnableExperimentation.Header" xml:space="preserve">
<value>Enable experimentation</value>
</data>
<data name="AllAppsTxt.Text" xml:space="preserve"> <data name="AllAppsTxt.Text" xml:space="preserve">
<value>All apps</value> <value>All apps</value>
</data> </data>

View File

@ -125,6 +125,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
_startup = GeneralSettingsConfig.Startup; _startup = GeneralSettingsConfig.Startup;
_autoDownloadUpdates = GeneralSettingsConfig.AutoDownloadUpdates; _autoDownloadUpdates = GeneralSettingsConfig.AutoDownloadUpdates;
_enableExperimentation = GeneralSettingsConfig.EnableExperimentation;
_isElevated = isElevated; _isElevated = isElevated;
_runElevated = GeneralSettingsConfig.RunElevated; _runElevated = GeneralSettingsConfig.RunElevated;
@ -152,6 +153,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private int _themeIndex; private int _themeIndex;
private bool _autoDownloadUpdates; private bool _autoDownloadUpdates;
private bool _enableExperimentation;
private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate; private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate;
private string _newAvailableVersion = string.Empty; private string _newAvailableVersion = string.Empty;
@ -284,6 +286,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
} }
} }
public bool EnableExperimentation
{
get
{
return _enableExperimentation;
}
set
{
if (_enableExperimentation != value)
{
_enableExperimentation = value;
GeneralSettingsConfig.EnableExperimentation = value;
NotifyPropertyChanged();
}
}
}
public static bool AutoUpdatesEnabled public static bool AutoUpdatesEnabled
{ {
get get

View File

@ -369,8 +369,13 @@
</labs:SettingsCard> </labs:SettingsCard>
</labs:SettingsExpander.Items> </labs:SettingsExpander.Items>
</labs:SettingsExpander> </labs:SettingsExpander>
<labs:SettingsCard x:Uid="GeneralPage_EnableExperimentation"
HeaderIcon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsExperimentation.png}">
<ToggleSwitch
x:Uid="ToggleSwitch"
IsOn="{Binding Mode=TwoWay, Path=EnableExperimentation}" />
</labs:SettingsCard>
</controls:SettingsGroup> </controls:SettingsGroup>
<InfoBar <InfoBar
x:Uid="General_SettingsBackupMessageResults" x:Uid="General_SettingsBackupMessageResults"
Title="{Binding SettingsBackupMessage}" Title="{Binding SettingsBackupMessage}"