1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Plugin settings - Restart Artemis for plugins that require admin rights

Setup wizard - Restart Artemis device providers that require admin rights
Setup wizard - Gracefully handle errors during device provider enable
Profile editor - Select a valid default brush on clean installs
This commit is contained in:
Robert 2021-03-11 20:27:54 +01:00
parent b917f36978
commit 985a94dbaf
16 changed files with 363 additions and 151 deletions

View File

@ -71,7 +71,7 @@ namespace Artemis.Core.Services
Plugin ImportPlugin(string fileName);
/// <summary>
/// Unloads and permanently removes the provided plugin
/// Unloads and permanently removes the provided plugin
/// </summary>
/// <param name="plugin">The plugin to remove</param>
void RemovePlugin(Plugin plugin);
@ -127,6 +127,13 @@ namespace Artemis.Core.Services
/// <returns></returns>
DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
/// <summary>
/// Queues an action for the provided plugin for the next time Artemis starts, before plugins are loaded
/// </summary>
/// <param name="plugin">The plugin to queue the action for</param>
/// <param name="pluginAction">The action to take</param>
void QueuePluginAction(Plugin plugin, PluginManagementAction pluginAction);
#region Events
/// <summary>

View File

@ -27,6 +27,7 @@ namespace Artemis.Core.Services
private readonly ILogger _logger;
private readonly IPluginRepository _pluginRepository;
private readonly List<Plugin> _plugins;
private bool _isElevated;
public PluginManagementService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository)
{
@ -34,6 +35,8 @@ namespace Artemis.Core.Services
_logger = logger;
_pluginRepository = pluginRepository;
_plugins = new List<Plugin>();
ProcessQueuedActions();
}
private void CopyBuiltInPlugin(FileInfo zipFileInfo, ZipArchive zipArchive)
@ -181,6 +184,7 @@ namespace Artemis.Core.Services
if (LoadingPlugins)
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
_isElevated = isElevated;
LoadingPlugins = true;
// Unload all currently loaded plugins first
@ -203,7 +207,7 @@ namespace Artemis.Core.Services
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
_logger.Debug("Loaded {count} plugin(s)", _plugins.Count);
bool adminRequired = _plugins.Any(p => p.Entity.IsEnabled && p.Info.RequiresAdmin);
bool adminRequired = _plugins.Any(p => p.Info.RequiresAdmin && p.Entity.IsEnabled && p.Entity.Features.Any(f => f.IsEnabled));
if (!isElevated && adminRequired)
{
_logger.Information("Restarting because one or more plugins requires elevation");
@ -336,6 +340,19 @@ namespace Artemis.Core.Services
if (plugin.Assembly == null)
throw new ArtemisPluginException(plugin, "Cannot enable a plugin that hasn't successfully been loaded");
if (plugin.Info.RequiresAdmin && plugin.Entity.Features.Any(f => f.IsEnabled) && !_isElevated)
{
if (!saveState)
throw new ArtemisCoreException("Cannot enable a plugin that requires elevation without saving it's state.");
plugin.Entity.IsEnabled = true;
SavePlugin(plugin);
_logger.Information("Restarting because a newly enabled plugin requires elevation");
Utilities.Restart(true, TimeSpan.FromMilliseconds(500));
return;
}
// Create the Ninject child kernel and load the module
plugin.Kernel = new ChildKernel(_kernel, new PluginModule(plugin));
OnPluginEnabling(new PluginEventArgs(plugin));
@ -523,6 +540,21 @@ namespace Artemis.Core.Services
_logger.Verbose("Enabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
OnPluginFeatureEnabling(new PluginFeatureEventArgs(pluginFeature));
if (pluginFeature.Plugin.Info.RequiresAdmin && !_isElevated)
{
if (!saveState)
throw new ArtemisCoreException("Cannot enable a feature that requires elevation without saving it's state.");
pluginFeature.Entity.IsEnabled = true;
pluginFeature.Plugin.Entity.IsEnabled = true;
SavePlugin(pluginFeature.Plugin);
_logger.Information("Restarting because a newly enabled feature requires elevation");
Utilities.Restart(true, TimeSpan.FromMilliseconds(500));
return;
}
try
{
pluginFeature.SetEnabled(true, isAutoEnable);
@ -586,6 +618,34 @@ namespace Artemis.Core.Services
#endregion
#region Queued actions
public void QueuePluginAction(Plugin plugin, PluginManagementAction pluginAction)
{
List<PluginQueuedActionEntity> existing = _pluginRepository.GetQueuedActions(plugin.Guid);
if (existing.Any(e => pluginAction == PluginManagementAction.Delete && e is PluginQueuedDeleteEntity))
return;
if (pluginAction == PluginManagementAction.Delete)
_pluginRepository.AddQueuedAction(new PluginQueuedDeleteEntity {PluginGuid = plugin.Guid, Directory = plugin.Directory.FullName});
}
private void ProcessQueuedActions()
{
foreach (PluginQueuedActionEntity pluginQueuedActionEntity in _pluginRepository.GetQueuedActions())
{
if (pluginQueuedActionEntity is PluginQueuedDeleteEntity deleteAction)
{
if (Directory.Exists(deleteAction.Directory))
Directory.Delete(deleteAction.Directory, true);
}
_pluginRepository.RemoveQueuedAction(pluginQueuedActionEntity);
}
}
#endregion
#region Storage
private void SavePlugin(Plugin plugin)
@ -673,4 +733,20 @@ namespace Artemis.Core.Services
#endregion
}
/// <summary>
/// Represents a type of plugin management action
/// </summary>
public enum PluginManagementAction
{
/// <summary>
/// A plugin management action that removes a plugin
/// </summary>
Delete,
// /// <summary>
// /// A plugin management action that updates a plugin
// /// </summary>
// Update
}
}

View File

@ -13,4 +13,21 @@ namespace Artemis.Storage.Entities.Plugins
public string Name { get; set; }
public string Value { get; set; }
}
/// <summary>
/// Represents a queued action for a plugin
/// </summary>
public abstract class PluginQueuedActionEntity
{
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
}
/// <summary>
/// Represents a queued delete action for a plugin
/// </summary>
public class PluginQueuedDeleteEntity : PluginQueuedActionEntity
{
public string Directory { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Artemis.Storage.Entities.Plugins;
namespace Artemis.Storage.Repositories.Interfaces
@ -8,9 +9,15 @@ namespace Artemis.Storage.Repositories.Interfaces
void AddPlugin(PluginEntity pluginEntity);
PluginEntity GetPluginByGuid(Guid pluginGuid);
void SavePlugin(PluginEntity pluginEntity);
void AddSetting(PluginSettingEntity pluginSettingEntity);
PluginSettingEntity GetSettingByGuid(Guid pluginGuid);
PluginSettingEntity GetSettingByNameAndGuid(string name, Guid pluginGuid);
void SaveSetting(PluginSettingEntity pluginSettingEntity);
void AddQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity);
List<PluginQueuedActionEntity> GetQueuedActions();
List<PluginQueuedActionEntity> GetQueuedActions(Guid pluginGuid);
void RemoveQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity);
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
@ -14,6 +15,7 @@ namespace Artemis.Storage.Repositories
_repository = repository;
_repository.Database.GetCollection<PluginSettingEntity>().EnsureIndex(s => new {s.Name, s.PluginGuid}, true);
_repository.Database.GetCollection<PluginQueuedActionEntity>().EnsureIndex(s => s.PluginGuid);
}
public void AddPlugin(PluginEntity pluginEntity)
@ -51,5 +53,24 @@ namespace Artemis.Storage.Repositories
{
_repository.Upsert(pluginSettingEntity);
}
public List<PluginQueuedActionEntity> GetQueuedActions()
{
return _repository.Query<PluginQueuedActionEntity>().ToList();
}
public List<PluginQueuedActionEntity> GetQueuedActions(Guid pluginGuid)
{
return _repository.Query<PluginQueuedActionEntity>().Where(q => q.PluginGuid == pluginGuid).ToList();
}
public void AddQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity)
{
_repository.Upsert(pluginQueuedActionEntity);
}
public void RemoveQueuedAction(PluginQueuedActionEntity pluginQueuedActionEntity)
{
_repository.Delete<PluginQueuedActionEntity>(pluginQueuedActionEntity.Id);
}
}
}

View File

@ -39,7 +39,7 @@ namespace Artemis.UI.Ninject.Factories
public interface ISettingsVmFactory : IVmFactory
{
PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin);
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo);
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield);
DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device);
}

View File

@ -3,17 +3,20 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Xml.Linq;
using Artemis.Core;
using Artemis.Core.LayerBrushes;
using Artemis.Core.Services;
using Artemis.Core.Services.Core;
using Artemis.UI.Properties;
using Artemis.UI.Screens.StartupWizard;
using Artemis.UI.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Utilities;
using Ninject;
using Serilog.Events;
using Stylet;
@ -70,7 +73,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
{
LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
BrushType = "ColorBrush"
BrushType = "SolidBrush"
});
WebServerPortSetting = _settingsService.GetSetting("WebServer.Port", 9696);
@ -306,7 +309,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
if (File.Exists(autoRunFile))
File.Delete(autoRunFile);
// TODO: Don't do anything if running a development build, only auto-run release builds
if (Constants.BuildInfo.IsLocalBuild)
return;
// Create or remove the task if necessary
try
@ -314,26 +318,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
bool taskCreated = false;
if (!recreate)
{
Process schtasks = new()
{
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = "/TN \"Artemis 2 autorun\""
}
};
schtasks.Start();
schtasks.WaitForExit();
taskCreated = schtasks.ExitCode == 0;
taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
}
if (StartWithWindows && !taskCreated)
CreateAutoRunTask();
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(AutoRunDelay));
else if (!StartWithWindows && taskCreated)
RemoveAutoRunTask();
SettingsUtilities.RemoveAutoRunTask();
}
catch (Exception e)
{
@ -341,71 +332,10 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
throw;
}
}
private void CreateAutoRunTask()
{
XDocument document = XDocument.Parse(Resources.artemis_autorun);
XElement task = document.Descendants().First();
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Date")
.SetValue(DateTime.Now);
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Author")
.SetValue(WindowsIdentity.GetCurrent().Name);
task.Descendants().First(d => d.Name.LocalName == "Triggers").Descendants().First(d => d.Name.LocalName == "LogonTrigger").Descendants().First(d => d.Name.LocalName == "Delay")
.SetValue(TimeSpan.FromSeconds(AutoRunDelay));
task.Descendants().First(d => d.Name.LocalName == "Principals").Descendants().First(d => d.Name.LocalName == "Principal").Descendants().First(d => d.Name.LocalName == "UserId")
.SetValue(WindowsIdentity.GetCurrent().User.Value);
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "WorkingDirectory")
.SetValue(Constants.ApplicationFolder);
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "Command")
.SetValue("\"" + Constants.ExecutablePath + "\"");
string xmlPath = Path.GetTempFileName();
using (Stream fileStream = new FileStream(xmlPath, FileMode.Create))
{
document.Save(fileStream);
}
Process schtasks = new()
{
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
Verb = "runas",
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = $"/Create /XML \"{xmlPath}\" /tn \"Artemis 2 autorun\" /F"
}
};
schtasks.Start();
schtasks.WaitForExit();
File.Delete(xmlPath);
}
private void RemoveAutoRunTask()
{
Process schtasks = new()
{
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
Verb = "runas",
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = "/Delete /TN \"Artemis 2 autorun\" /f"
}
};
schtasks.Start();
schtasks.WaitForExit();
}
}
public enum ApplicationColorScheme
{
Light,

View File

@ -45,8 +45,17 @@
<TextBlock Grid.Column="1" Text="{Binding FeatureInfo.Name}" Style="{StaticResource MaterialDesignTextBlock}" VerticalAlignment="Center" ToolTip="{Binding FeatureInfo.Description}" />
<!-- Enable toggle column -->
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="8"
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}">
<StackPanel Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="8"
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}"
Orientation="Horizontal">
<materialDesign:PackIcon Kind="ShieldHalfFull"
ToolTip="Plugin requires admin rights"
VerticalAlignment="Center"
Margin="0 0 5 0"
Visibility="{Binding ShowShield, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding IsEnabled}" IsEnabled="{Binding FeatureInfo.Plugin.IsEnabled}">
Feature enabled
</CheckBox>

View File

@ -11,26 +11,33 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
{
public class PluginFeatureViewModel : Screen
{
private readonly ICoreService _coreService;
private readonly IDialogService _dialogService;
private readonly IPluginManagementService _pluginManagementService;
private bool _enabling;
private readonly IMessageService _messageService;
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
bool showShield,
ICoreService coreService,
IDialogService dialogService,
IPluginManagementService pluginManagementService,
IMessageService messageService)
{
_coreService = coreService;
_dialogService = dialogService;
_pluginManagementService = pluginManagementService;
_messageService = messageService;
FeatureInfo = pluginFeatureInfo;
ShowShield = FeatureInfo.Plugin.Info.RequiresAdmin && showShield;
}
public PluginFeatureInfo FeatureInfo { get; }
public Exception LoadException => FeatureInfo.Instance?.LoadException;
public bool ShowShield { get; }
public bool Enabling
{
get => _enabling;
@ -95,6 +102,16 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
try
{
if (FeatureInfo.Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
{
bool confirmed = await _dialogService.ShowConfirmDialog("Enable feature", "The plugin of this feature requires admin rights, are you sure you want to enable it?");
if (!confirmed)
{
NotifyOfPropertyChange(nameof(IsEnabled));
return;
}
}
await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance, true));
}
catch (Exception e)

View File

@ -118,7 +118,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
protected override void OnInitialActivate()
{
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo));
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
base.OnInitialActivate();
}

View File

@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mde="https://spiegelp.github.io/MaterialDesignExtensions/winfx/xaml"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
Width="800"
Height="600"
@ -17,47 +18,58 @@
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
UseLayoutRounding="True"
d:DesignHeight="450" d:DesignWidth="800">
<mde:Stepper IsLinear="True" Layout="Horizontal" ActiveStepChanged="{s:Action ActiveStepChanged}" CancelNavigation="{s:Action SkipOrFinishWizard}" Margin="15">
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Welcome" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Devices" SecondLevelTitle="Pick your brands" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Desktop layout" SecondLevelTitle="Map your surface" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Settings" SecondLevelTitle="Set your preferences" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Finish" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
</mde:Stepper>
<materialDesign:DialogHost IsTabStop="False"
Focusable="False"
Identifier="RootDialog"
DialogTheme="Inherit"
SnackbarMessageQueue="{Binding MainMessageQueue}">
<Grid>
<mde:Stepper IsLinear="True" Layout="Horizontal" ActiveStepChanged="{s:Action ActiveStepChanged}" CancelNavigation="{s:Action SkipOrFinishWizard}" Margin="15">
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Welcome" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Devices" SecondLevelTitle="Pick your brands" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Desktop layout" SecondLevelTitle="Map your surface" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Settings" SecondLevelTitle="Set your preferences" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
<mde:Step>
<mde:Step.Header>
<mde:StepTitleHeader FirstLevelTitle="Finish" />
</mde:Step.Header>
<mde:Step.Content>
<ContentControl s:View.Model="{Binding ActiveItem}" />
</mde:Step.Content>
</mde:Step>
</mde:Stepper>
<materialDesign:Snackbar x:Name="MainSnackbar"
MessageQueue="{Binding MainMessageQueue}"
materialDesign:SnackbarMessage.InlineActionButtonMaxHeight="80"
materialDesign:SnackbarMessage.ContentMaxHeight="200" />
</Grid>
</materialDesign:DialogHost>
</mde:MaterialWindow>

View File

@ -2,18 +2,22 @@
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Screens.StartupWizard.Steps;
using Artemis.UI.Shared.Services;
using MaterialDesignExtensions.Controllers;
using MaterialDesignExtensions.Controls;
using MaterialDesignThemes.Wpf;
using Stylet;
namespace Artemis.UI.Screens.StartupWizard
{
public class StartupWizardViewModel : Conductor<Screen>.Collection.OneActive
{
private readonly IMessageService _messageService;
private readonly ISettingsService _settingsService;
private StepperController _stepperController;
public StartupWizardViewModel(ISettingsService settingsService,
IMessageService messageService,
WelcomeStepViewModel welcome,
DevicesStepViewModel devices,
LayoutStepViewModel layout,
@ -21,6 +25,7 @@ namespace Artemis.UI.Screens.StartupWizard
FinishStepViewModel finish)
{
_settingsService = settingsService;
_messageService = messageService;
Items.Add(welcome);
Items.Add(devices);
Items.Add(layout);
@ -30,6 +35,8 @@ namespace Artemis.UI.Screens.StartupWizard
ActiveItem = Items.First();
}
public ISnackbarMessageQueue MainMessageQueue { get; set; }
public void ActiveStepChanged(object sender, ActiveStepChangedEventArgs e)
{
Stepper stepper = (Stepper) sender;
@ -40,22 +47,23 @@ namespace Artemis.UI.Screens.StartupWizard
}
public void SkipOrFinishWizard()
{
RequestClose();
}
protected override void OnClose()
{
PluginSetting<bool> setting = _settingsService.GetSetting("UI.SetupWizardCompleted", false);
setting.Value = true;
setting.Save();
base.OnClose();
RequestClose();
}
public void Continue()
{
_stepperController.Continue();
}
protected override void OnInitialActivate()
{
MainMessageQueue = _messageService.MainMessageQueue;
base.OnInitialActivate();
}
}
}

View File

@ -16,7 +16,7 @@
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource MaterialDesignBody1TextBlock}" TextWrapping="Wrap">
EnabledDevices are supported through the use of device providers. <LineBreak />
Devices are supported through the use of device providers. <LineBreak />
In the list below you can enable device providers for each brand you own by checking <Run Text="Feature enabled" FontWeight="Bold" />.
</TextBlock>

View File

@ -31,7 +31,7 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
IEnumerable<PluginFeatureInfo> features = _pluginManagementService.GetAllPlugins()
.SelectMany(p => p.Features.Where(f => typeof(DeviceProvider).IsAssignableFrom(f.FeatureType)))
.OrderBy(d => d.GetType().Name);
Items.AddRange(features.Select(d => _settingsVmFactory.CreatePluginFeatureViewModel(d)));
Items.AddRange(features.Select(d => _settingsVmFactory.CreatePluginFeatureViewModel(d, true)));
base.OnActivate();
}

View File

@ -7,6 +7,7 @@ using Artemis.UI.Screens.Settings.Tabs.General;
using Artemis.UI.Screens.Settings.Tabs.Plugins;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Utilities;
using Stylet;
namespace Artemis.UI.Screens.StartupWizard.Steps
@ -34,7 +35,7 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
_settingsService.GetSetting("UI.AutoRun", false).Value = value;
_settingsService.GetSetting("UI.AutoRun", false).Save();
NotifyOfPropertyChange(nameof(StartWithWindows));
Task.Run(ApplyAutorun);
Task.Run(() => ApplyAutorun(false));
}
}
@ -59,23 +60,35 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
}
}
private void ApplyAutorun()
private void ApplyAutorun(bool recreate)
{
if (!StartWithWindows)
StartMinimized = false;
// Remove the old auto-run method of placing a shortcut in shell:startup
string autoRunFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Artemis.lnk");
if (File.Exists(autoRunFile))
File.Delete(autoRunFile);
// Local builds shouldn't auto-run, this is just annoying
// if (Constants.BuildInfo.IsLocalBuild)
// return;
// Create or remove the task if necessary
try
{
string autoRunFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Artemis.lnk");
string executableFile = Constants.ExecutablePath;
bool taskCreated = false;
if (!recreate)
taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
if (File.Exists(autoRunFile))
File.Delete(autoRunFile);
if (StartWithWindows)
ShortcutUtilities.Create(autoRunFile, executableFile, "--autorun", new FileInfo(executableFile).DirectoryName, "Artemis", "", "");
else
StartMinimized = false;
if (StartWithWindows && !taskCreated)
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(15));
else if (!StartWithWindows && taskCreated)
SettingsUtilities.RemoveAutoRunTask();
}
catch (Exception e)
{
_dialogService.ShowExceptionDialog("An exception occured while trying to apply the auto run setting", e);
Execute.PostToUIThread(() => _dialogService.ShowExceptionDialog("An exception occured while trying to apply the auto run setting", e));
throw;
}
}

View File

@ -0,0 +1,95 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Xml.Linq;
using Artemis.Core;
using Artemis.UI.Properties;
namespace Artemis.UI.Utilities
{
public static class SettingsUtilities
{
public static bool IsAutoRunTaskCreated()
{
Process schtasks = new()
{
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = "/TN \"Artemis 2 autorun\""
}
};
schtasks.Start();
schtasks.WaitForExit();
return schtasks.ExitCode == 0;
}
public static void CreateAutoRunTask(TimeSpan autoRunDelay)
{
XDocument document = XDocument.Parse(Resources.artemis_autorun);
XElement task = document.Descendants().First();
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Date")
.SetValue(DateTime.Now);
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Author")
.SetValue(WindowsIdentity.GetCurrent().Name);
task.Descendants().First(d => d.Name.LocalName == "Triggers").Descendants().First(d => d.Name.LocalName == "LogonTrigger").Descendants().First(d => d.Name.LocalName == "Delay")
.SetValue(autoRunDelay);
task.Descendants().First(d => d.Name.LocalName == "Principals").Descendants().First(d => d.Name.LocalName == "Principal").Descendants().First(d => d.Name.LocalName == "UserId")
.SetValue(WindowsIdentity.GetCurrent().User.Value);
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "WorkingDirectory")
.SetValue(Constants.ApplicationFolder);
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "Command")
.SetValue("\"" + Constants.ExecutablePath + "\"");
string xmlPath = Path.GetTempFileName();
using (Stream fileStream = new FileStream(xmlPath, FileMode.Create))
{
document.Save(fileStream);
}
Process schtasks = new()
{
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
Verb = "runas",
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = $"/Create /XML \"{xmlPath}\" /tn \"Artemis 2 autorun\" /F"
}
};
schtasks.Start();
schtasks.WaitForExit();
File.Delete(xmlPath);
}
public static void RemoveAutoRunTask()
{
Process schtasks = new()
{
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
Verb = "runas",
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = "/Delete /TN \"Artemis 2 autorun\" /f"
}
};
schtasks.Start();
schtasks.WaitForExit();
}
}
}