mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
[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:
27
src/common/AllExperiments/AllExperiments.csproj
Normal file
27
src/common/AllExperiments/AllExperiments.csproj
Normal 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>
|
||||
211
src/common/AllExperiments/Experiments.cs
Normal file
211
src/common/AllExperiments/Experiments.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
src/common/AllExperiments/Logger.cs
Normal file
81
src/common/AllExperiments/Logger.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
static std::wstring settings_theme = L"system";
|
||||
static bool run_as_elevated = false;
|
||||
static bool download_updates_automatically = true;
|
||||
static bool enable_experimentation = true;
|
||||
|
||||
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"run_elevated", json::value(isRunElevated));
|
||||
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"theme", json::value(theme));
|
||||
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);
|
||||
download_updates_automatically = loaded.GetNamedBoolean(L"download_updates_automatically", true) && check_user_is_admin();
|
||||
enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation",true);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
@@ -67,6 +70,7 @@ GeneralSettings get_general_settings()
|
||||
.isRunElevated = run_as_elevated,
|
||||
.isAdmin = is_user_admin,
|
||||
.downloadUpdatesAutomatically = download_updates_automatically && is_user_admin,
|
||||
.enableExperimentation = enable_experimentation,
|
||||
.theme = settings_theme,
|
||||
.systemTheme = WindowsColors::is_dark_mode() ? L"dark" : L"light",
|
||||
.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);
|
||||
|
||||
enable_experimentation = general_configs.GetNamedBoolean(L"enable_experimentation", true);
|
||||
|
||||
if (json::has(general_configs, L"startup", json::JsonValueType::Boolean))
|
||||
{
|
||||
const bool startup = general_configs.GetNamedBoolean(L"startup");
|
||||
|
||||
@@ -11,6 +11,7 @@ struct GeneralSettings
|
||||
bool isRunElevated;
|
||||
bool isAdmin;
|
||||
bool downloadUpdatesAutomatically;
|
||||
bool enableExperimentation;
|
||||
std::wstring theme;
|
||||
std::wstring systemTheme;
|
||||
std::wstring powerToysVersion;
|
||||
|
||||
@@ -56,6 +56,7 @@ void Trace::SettingsChanged(const GeneralSettings& settings)
|
||||
TraceLoggingWideString(enabledModules.c_str(), "ModulesEnabled"),
|
||||
TraceLoggingBoolean(settings.isRunElevated, "AlwaysRunElevated"),
|
||||
TraceLoggingBoolean(settings.downloadUpdatesAutomatically, "DownloadUpdatesAutomatically"),
|
||||
TraceLoggingBoolean(settings.enableExperimentation, "EnableExperimentation"),
|
||||
TraceLoggingWideString(settings.theme.c_str(), "Theme"),
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
|
||||
@@ -49,12 +49,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("download_updates_automatically")]
|
||||
public bool AutoDownloadUpdates { get; set; }
|
||||
|
||||
[JsonPropertyName("enable_experimentation")]
|
||||
public bool EnableExperimentation { get; set; }
|
||||
|
||||
public GeneralSettings()
|
||||
{
|
||||
Startup = false;
|
||||
IsAdmin = false;
|
||||
IsElevated = false;
|
||||
AutoDownloadUpdates = false;
|
||||
EnableExperimentation = true;
|
||||
Theme = "system";
|
||||
SystemTheme = "light";
|
||||
try
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<Version>$(Version).0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 931 B |
@@ -10,12 +10,13 @@
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="280" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image
|
||||
x:Name="HeaderImage"
|
||||
Height="{x:Bind HeroImageHeight}"
|
||||
Source="{x:Bind HeroImage}"
|
||||
Stretch="UniformToFill" />
|
||||
|
||||
@@ -23,9 +24,7 @@
|
||||
Grid.Row="1"
|
||||
Padding="32,24,32,24"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical">
|
||||
<StackPanel VerticalAlignment="Top" Orientation="Vertical">
|
||||
|
||||
<TextBlock
|
||||
x:Name="TitleTxt"
|
||||
@@ -49,4 +48,4 @@
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -32,6 +32,12 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
set => SetValue(HeroImageProperty, value);
|
||||
}
|
||||
|
||||
public double HeroImageHeight
|
||||
{
|
||||
get { return (double)GetValue(HeroImageHeightProperty); }
|
||||
set { SetValue(HeroImageHeightProperty, value); }
|
||||
}
|
||||
|
||||
public object PageContent
|
||||
{
|
||||
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 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 HeroImageHeightProperty = DependencyProperty.Register("HeroImageHeight", typeof(double), typeof(SettingsPageControl), new PropertyMetadata(280.0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using AllExperiments;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
|
||||
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
|
||||
@@ -47,10 +49,16 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
|
||||
public ObservableCollection<OobePowerToysModule> Modules { get; }
|
||||
|
||||
private static ISettingsUtils settingsUtils = new SettingsUtils();
|
||||
|
||||
private bool ExperimentationToggleSwitchEnabled { get; set; } = true;
|
||||
|
||||
public OobeShellPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ExperimentationToggleSwitchEnabled = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils).SettingsConfig.EnableExperimentation;
|
||||
|
||||
DataContext = ViewModel;
|
||||
OobeShellHandler = this;
|
||||
UpdateUITheme();
|
||||
@@ -186,7 +194,27 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
{
|
||||
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 "AlwaysOnTop": NavigationFrame.Navigate(typeof(OobeAlwaysOnTop)); break;
|
||||
case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break;
|
||||
|
||||
@@ -55,6 +55,9 @@
|
||||
<PropertyGroup>
|
||||
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json</RestoreAdditionalProjectSources>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="OOBE\Views\OobeOverviewPlaceholder.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Controls\ColorFormatEditor.xaml" />
|
||||
</ItemGroup>
|
||||
@@ -91,15 +94,19 @@
|
||||
<!-- 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
|
||||
package has not yet been restored -->
|
||||
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\AllExperiments\AllExperiments.csproj" />
|
||||
<ProjectReference Include="..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="FlyoutWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -122,10 +129,17 @@
|
||||
<None Update="icon.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Page Update="OOBE\Views\OobeOverviewPlaceholder.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Update="OOBE\Views\OobeHosts.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Update="OOBE\Views\OobeOverviewAlternate.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="OOBE\Views\OobePowerOCR.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
|
||||
@@ -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>
|
||||
<comment>"Host File Editor" is a product name</comment>
|
||||
</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">
|
||||
<value>Position of additional content</value>
|
||||
</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">
|
||||
<value>Preferred language</value>
|
||||
</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">
|
||||
<value>All apps</value>
|
||||
</data>
|
||||
|
||||
@@ -125,6 +125,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
_startup = GeneralSettingsConfig.Startup;
|
||||
_autoDownloadUpdates = GeneralSettingsConfig.AutoDownloadUpdates;
|
||||
_enableExperimentation = GeneralSettingsConfig.EnableExperimentation;
|
||||
|
||||
_isElevated = isElevated;
|
||||
_runElevated = GeneralSettingsConfig.RunElevated;
|
||||
@@ -152,6 +153,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private int _themeIndex;
|
||||
|
||||
private bool _autoDownloadUpdates;
|
||||
private bool _enableExperimentation;
|
||||
|
||||
private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate;
|
||||
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
|
||||
{
|
||||
get
|
||||
|
||||
@@ -369,8 +369,13 @@
|
||||
</labs:SettingsCard>
|
||||
</labs:SettingsExpander.Items>
|
||||
</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>
|
||||
|
||||
<InfoBar
|
||||
x:Uid="General_SettingsBackupMessageResults"
|
||||
Title="{Binding SettingsBackupMessage}"
|
||||
|
||||
Reference in New Issue
Block a user