Profile adaption - Added support for device hint to 'all' devices

Layouts - Round LED sizes to integers
Auto-update - Use modern Windows 10 toasts
UI - On shutdown wait 6 seconds longer before force-shutdown
UI - Use black icons in notifications on white Windows theme
Message service - Added optional toast callbacks
This commit is contained in:
Robert committed 2021-05-14 21:49:10 +02:00
1 parent 5c337e9c5e
commit 96c55e5c03
26 files changed
+886 -674

No files matched your search

@@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Artemis.Core.DataModelExpansions;
using Artemis.Storage.Entities.Profile;
using Newtonsoft.Json;
using SkiaSharp;
namespace Artemis.Core.Modules
@@ -91,11 +94,13 @@ namespace Artemis.Core.Modules
/// </summary>
public abstract class ProfileModule : Module
{
private readonly List<ProfileDescriptor> _defaultProfiles;
private readonly object _lock = new();
/// <summary>
/// Gets a list of all properties ignored at runtime using <c>IgnoreProperty(x => x.y)</c>
/// </summary>
protected internal readonly List<PropertyInfo> HiddenPropertiesList = new();
private readonly object _lock = new();
/// <summary>
/// Creates a new instance of the <see cref="ProfileModule" /> class
@@ -103,6 +108,7 @@ namespace Artemis.Core.Modules
protected ProfileModule()
{
OpacityOverride = 1;
_defaultProfiles = new List<ProfileDescriptor>();
}
/// <summary>
@@ -130,6 +136,11 @@ namespace Artemis.Core.Modules
/// </summary>
public bool AnimatingProfileChange { get; private set; }
/// <summary>
/// Gets a list of default profiles, to add a new default profile use <see cref="AddDefaultProfile" />
/// </summary>
public ReadOnlyCollection<ProfileDescriptor> DefaultProfiles => _defaultProfiles.AsReadOnly();
/// <summary>
/// Called after the profile has updated
/// </summary>
@@ -148,6 +159,40 @@ namespace Artemis.Core.Modules
{
}
/// <summary>
/// Occurs when the <see cref="ActiveProfile" /> has changed
/// </summary>
public event EventHandler? ActiveProfileChanged;
/// <summary>
/// Adds a default profile by reading it from the file found at the provided path
/// </summary>
/// <param name="file"></param>
protected void AddDefaultProfile(string file)
{
// Ensure the file exists
if (!File.Exists(file))
throw new ArtemisPluginFeatureException(this, $"Could not find default profile at {file}.");
// Deserialize and make sure that succeeded
ProfileEntity? profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(File.ReadAllText(file));
if (profileEntity == null)
throw new ArtemisPluginFeatureException(this, $"Failed to deserialize default profile at {file}.");
// Ensure the profile ID is unique
ProfileDescriptor descriptor = new(this, profileEntity) {NeedsAdaption = true};
if (_defaultProfiles.Any(d => d.Id == descriptor.Id))
throw new ArtemisPluginFeatureException(this, $"Cannot add default profile from {file}, profile ID {descriptor.Id} already in use.");
_defaultProfiles.Add(descriptor);
}
/// <summary>
/// Invokes the <see cref="ActiveProfileChanged" /> event
/// </summary>
protected virtual void OnActiveProfileChanged()
{
ActiveProfileChanged?.Invoke(this, EventArgs.Empty);
}
internal override void InternalUpdate(double deltaTime)
{
StartUpdateMeasure();
@@ -245,22 +290,5 @@ namespace Artemis.Core.Modules
base.Deactivate(isDeactivateOverride);
Activate(isActivateOverride);
}
#region Events
/// <summary>
/// Occurs when the <see cref="ActiveProfile" /> has changed
/// </summary>
public event EventHandler? ActiveProfileChanged;
/// <summary>
/// Invokes the <see cref="ActiveProfileChanged" /> event
/// </summary>
protected virtual void OnActiveProfileChanged()
{
ActiveProfileChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
}
}
@@ -23,7 +23,7 @@ namespace Artemis.Core
public abstract string Description { get; }
/// <summary>
/// Gets a boolean indicating whether installing or uninstalling this prerequisite requires admin privileges
/// [NYI] Gets a boolean indicating whether installing or uninstalling this prerequisite requires admin privileges
/// </summary>
public abstract bool RequiresElevation { get; }
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
@@ -17,11 +18,13 @@ namespace Artemis.Core
/// <param name="fileName">The target file to execute</param>
/// <param name="arguments">A set of command-line arguments to use when starting the application</param>
/// <param name="waitForExit">A boolean indicating whether the action should wait for the process to exit</param>
public ExecuteFileAction(string name, string fileName, string? arguments = null, bool waitForExit = true) : base(name)
/// <param name="elevate">A boolean indicating whether the file should run with administrator privileges (does not require <see cref="PluginPrerequisite.RequiresElevation"/>)</param>
public ExecuteFileAction(string name, string fileName, string? arguments = null, bool waitForExit = true, bool elevate = false) : base(name)
{
FileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
Arguments = arguments;
WaitForExit = waitForExit;
Elevate = elevate;
}
/// <summary>
@@ -30,7 +33,7 @@ namespace Artemis.Core
public string FileName { get; }
/// <summary>
/// Gets a set of command-line arguments to use when starting the application
/// Gets a set of command-line arguments to use when starting the application
/// </summary>
public string? Arguments { get; }
@@ -39,6 +42,11 @@ namespace Artemis.Core
/// </summary>
public bool WaitForExit { get; }
/// <summary>
/// Gets a boolean indicating whether the file should run with administrator privileges
/// </summary>
public bool Elevate { get; }
/// <inheritdoc />
public override async Task Execute(CancellationToken cancellationToken)
{
@@ -48,7 +56,7 @@ namespace Artemis.Core
ShowProgressBar = true;
ProgressIndeterminate = true;
int result = await RunProcessAsync(FileName, Arguments);
int result = await RunProcessAsync(FileName, Arguments, Elevate);
Status = $"{FileName} exited with code {result}";
}
@@ -64,13 +72,19 @@ namespace Artemis.Core
}
}
private static Task<int> RunProcessAsync(string fileName, string? arguments)
private static Task<int> RunProcessAsync(string fileName, string? arguments, bool elevate)
{
TaskCompletionSource<int> tcs = new();
Process process = new()
{
StartInfo = {FileName = fileName, Arguments = arguments!},
StartInfo =
{
FileName = fileName,
Arguments = arguments!,
Verb = elevate ? "RunAs" : "",
UseShellExecute = elevate
},
EnableRaisingEvents = true
};
@@ -80,7 +94,17 @@ namespace Artemis.Core
process.Dispose();
};
process.Start();
try
{
process.Start();
}
catch (Win32Exception e)
{
if (!elevate || e.NativeErrorCode != 0x4c7)
throw;
tcs.SetResult(-1);
}
return tcs.Task;
}