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

UI - Rename each OS assembly to Artemis 2

UI - Added auto-run
UI - Added auto-update
This commit is contained in:
Robert 2022-08-07 14:15:18 +02:00
parent 20af5e9c21
commit faf5302975
30 changed files with 1568 additions and 192 deletions

View File

@ -67,6 +67,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cprofiling/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cscriptingproviders_005Cscripts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Csettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=providers_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rgb_002Enet/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Ccolorquantizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Ccolorquantizer_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>

View File

@ -0,0 +1,23 @@
using Artemis.Core.SkiaSharp;
namespace Artemis.Core.Providers;
/// <summary>
/// Represents a class that can provide one or more graphics <see cref="IManagedGraphicsContext" /> instances by name.
/// </summary>
public interface IGraphicsContextProvider
{
/// <summary>
/// Gets the name of the graphics context.
/// </summary>
string GraphicsContextName { get; }
/// <summary>
/// Creates an instance of the managed graphics context this provider provides.
/// </summary>
/// <returns>
/// An instance of the resulting managed graphics context if successfully created; otherwise
/// <see langword="null" />.
/// </returns>
IManagedGraphicsContext? GetGraphicsContext();
}

View File

@ -1,22 +0,0 @@
using System.Collections.Generic;
using Artemis.Core.SkiaSharp;
namespace Artemis.Core.Services;
/// <summary>
/// Represents a class that can provide one or more graphics <see cref="IManagedGraphicsContext"/> instances by name.
/// </summary>
public interface IGraphicsContextProvider
{
/// <summary>
/// Gets a read only collection containing the names of all the graphics contexts supported by this provider.
/// </summary>
IReadOnlyCollection<string> GraphicsContextNames { get; }
/// <summary>
/// Gets a managed graphics context by name.
/// </summary>
/// <param name="name">The name of the graphics context.</param>
/// <returns>If found, an instance of the managed graphics context with the given <paramref name="name"/>; otherwise <see langword="null"/>.</returns>
IManagedGraphicsContext? GetGraphicsContext(string name);
}

View File

@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using Artemis.Core.DeviceProviders;
using Artemis.Core.Providers;
using Artemis.Core.Services.Models;
using Artemis.Core.SkiaSharp;
using Artemis.Storage.Entities.Surface;
@ -363,15 +364,15 @@ namespace Artemis.Core.Services
return;
}
IGraphicsContextProvider? provider = _kernel.TryGet<IGraphicsContextProvider>();
if (provider == null)
List<IGraphicsContextProvider> providers = _kernel.Get<List<IGraphicsContextProvider>>();
if (!providers.Any())
{
_logger.Warning("No graphics context provider found, defaulting to software rendering");
UpdateGraphicsContext(null);
return;
}
IManagedGraphicsContext? context = provider.GetGraphicsContext(_preferredGraphicsContext.Value);
IManagedGraphicsContext? context = providers.FirstOrDefault(p => p.GraphicsContextName == _preferredGraphicsContext.Value)?.GetGraphicsContext();
if (context == null)
{
_logger.Warning("No graphics context named '{Context}' found, defaulting to software rendering", _preferredGraphicsContext.Value);

View File

@ -5,21 +5,24 @@
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<Platforms>x64</Platforms>
<OutputPath>bin</OutputPath>
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
<AssemblyName>Artemis 2</AssemblyName>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**"/>
<None Remove=".gitignore"/>
<AvaloniaResource Include="Assets\**" />
<None Remove=".gitignore" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.15"/>
<PackageReference Include="Avalonia.Desktop" Version="0.10.15"/>
<PackageReference Include="Avalonia" Version="0.10.15" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15"/>
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
<PackageReference Include="ReactiveUI" Version="17.1.50" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj"/>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
</ItemGroup>
</Project>

View File

@ -5,21 +5,24 @@
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<Platforms>x64</Platforms>
<OutputPath>bin</OutputPath>
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
<AssemblyName>Artemis 2</AssemblyName>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**"/>
<None Remove=".gitignore"/>
<AvaloniaResource Include="Assets\**" />
<None Remove=".gitignore" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.15"/>
<PackageReference Include="Avalonia.Desktop" Version="0.10.15"/>
<PackageReference Include="Avalonia" Version="0.10.15" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15"/>
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
<PackageReference Include="ReactiveUI" Version="17.1.50" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj"/>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using System.Threading.Tasks;
namespace Artemis.UI.Shared.Providers;
/// <summary>
/// Represents a provider for custom cursors.
/// </summary>
public interface IAutoRunProvider
{
/// <summary>
/// Asynchronously enables auto-run.
/// </summary>
/// <param name="recreate">A boolean indicating whether the auto-run configuration should be recreated (the auto run delay changed)</param>
/// <param name="autoRunDelay">The delay in seconds before the application should start (if supported)</param>
Task EnableAutoRun(bool recreate, int autoRunDelay);
/// <summary>
/// Asynchronously disables auto-run.
/// </summary>
Task DisableAutoRun();
}

View File

@ -0,0 +1,31 @@
using System.Threading.Tasks;
namespace Artemis.UI.Shared.Providers;
/// <summary>
/// Represents a provider for custom cursors.
/// </summary>
public interface IUpdateProvider
{
/// <summary>
/// Asynchronously checks whether an update is available.
/// </summary>
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
/// <returns>A task returning <see langword="true" /> if an update is available; otherwise <see langword="false" />.</returns>
Task<bool> CheckForUpdate(string channel);
/// <summary>
/// Applies any available updates.
/// </summary>
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
/// <param name="silent">Whether or not to update silently.</param>
Task ApplyUpdate(string channel, bool silent);
/// <summary>
/// Offer to install the update to the user.
/// </summary>
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
/// <param name="windowOpen">A boolean indicating whether the main window is open.</param>
/// <returns>A task returning <see langword="true" /> if the user chose to update; otherwise <see langword="false" />.</returns>
Task OfferUpdate(string channel, bool windowOpen);
}

View File

@ -4,7 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
x:Class="Artemis.UI.Shared.Services.ExceptionDialogView"
Icon="/Assets/Images/Logo/application.ico"
Title="{Binding Title}"
ExtendClientAreaToDecorationsHint="True"
Width="800"

View File

@ -1,5 +1,6 @@
using Artemis.Core.Services;
using Artemis.UI.Windows.Ninject;
using Artemis.UI.Windows.Providers;
using Artemis.UI.Windows.Providers.Input;
using Avalonia;
using Avalonia.Controls;

View File

@ -1,42 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<Platforms>x64</Platforms>
<OutputPath>bin</OutputPath>
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
<Company>Artemis RGB</Company>
<Product>Artemis 2.0</Product>
<ApplicationIcon>..\Artemis.UI\Assets\Images\Logo\application.ico</ApplicationIcon>
<AssemblyName>Artemis 2</AssemblyName>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**"/>
<None Remove=".gitignore"/>
<None Remove="Assets\Cursors\aero_crosshair.cur"/>
<None Remove="Assets\Cursors\aero_crosshair_minus.cur"/>
<None Remove="Assets\Cursors\aero_crosshair_plus.cur"/>
<None Remove="Assets\Cursors\aero_drag.cur"/>
<None Remove="Assets\Cursors\aero_drag_ew.cur"/>
<None Remove="Assets\Cursors\aero_fill.cur"/>
<None Remove="Assets\Cursors\aero_pen_min.cur"/>
<None Remove="Assets\Cursors\aero_pen_plus.cur"/>
<None Remove="Assets\Cursors\aero_rotate.cur"/>
<None Remove="Assets\Cursors\aero_rotate_bl.cur"/>
<None Remove="Assets\Cursors\aero_rotate_br.cur"/>
<None Remove="Assets\Cursors\aero_rotate_tl.cur"/>
<None Remove="Assets\Cursors\aero_rotate_tr.cur"/>
<AvaloniaResource Include="Assets\**" />
<None Remove=".gitignore" />
<None Remove="Assets\Cursors\aero_crosshair.cur" />
<None Remove="Assets\Cursors\aero_crosshair_minus.cur" />
<None Remove="Assets\Cursors\aero_crosshair_plus.cur" />
<None Remove="Assets\Cursors\aero_drag.cur" />
<None Remove="Assets\Cursors\aero_drag_ew.cur" />
<None Remove="Assets\Cursors\aero_fill.cur" />
<None Remove="Assets\Cursors\aero_pen_min.cur" />
<None Remove="Assets\Cursors\aero_pen_plus.cur" />
<None Remove="Assets\Cursors\aero_rotate.cur" />
<None Remove="Assets\Cursors\aero_rotate_bl.cur" />
<None Remove="Assets\Cursors\aero_rotate_br.cur" />
<None Remove="Assets\Cursors\aero_rotate_tl.cur" />
<None Remove="Assets\Cursors\aero_rotate_tr.cur" />
<None Include="..\Artemis.UI\Assets\Images\Logo\application.ico">
<Link>application.ico</Link>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.15"/>
<PackageReference Include="Avalonia.Desktop" Version="0.10.15"/>
<PackageReference Include="Avalonia" Version="0.10.15" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15"/>
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
<PackageReference Include="Avalonia.Win32" Version="0.10.15"/>
<PackageReference Include="Microsoft.Win32" Version="2.0.1"/>
<PackageReference Include="RawInput.Sharp" Version="0.0.4"/>
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.1-preview.1"/>
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
<PackageReference Include="Avalonia.Win32" Version="0.10.15" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageReference Include="Microsoft.Win32" Version="2.0.1" />
<PackageReference Include="RawInput.Sharp" Version="0.0.4" />
<PackageReference Include="ReactiveUI" Version="17.1.50" />
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.1-preview.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj"/>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
</ItemGroup>
</Project>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

View File

@ -0,0 +1,279 @@
#nullable disable
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Artemis.UI.Windows.Models
{
public class DevOpsBuilds
{
[JsonProperty("count")]
public long Count { get; set; }
[JsonProperty("value")]
public List<DevOpsBuild> Builds { get; set; }
}
public class DevOpsBuild
{
[JsonProperty("_links")]
public BuildLinks Links { get; set; }
[JsonProperty("properties")]
public Properties Properties { get; set; }
[JsonProperty("tags")]
public List<object> Tags { get; set; }
[JsonProperty("validationResults")]
public List<object> ValidationResults { get; set; }
[JsonProperty("plans")]
public List<Plan> Plans { get; set; }
[JsonProperty("triggerInfo")]
public TriggerInfo TriggerInfo { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("buildNumber")]
public string BuildNumber { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("queueTime")]
public DateTimeOffset QueueTime { get; set; }
[JsonProperty("startTime")]
public DateTimeOffset StartTime { get; set; }
[JsonProperty("finishTime")]
public DateTimeOffset FinishTime { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("definition")]
public Definition Definition { get; set; }
[JsonProperty("buildNumberRevision")]
public long BuildNumberRevision { get; set; }
[JsonProperty("project")]
public Project Project { get; set; }
[JsonProperty("uri")]
public string Uri { get; set; }
[JsonProperty("sourceBranch")]
public string SourceBranch { get; set; }
[JsonProperty("sourceVersion")]
public string SourceVersion { get; set; }
[JsonProperty("priority")]
public string Priority { get; set; }
[JsonProperty("reason")]
public string Reason { get; set; }
[JsonProperty("requestedFor")]
public LastChangedBy RequestedFor { get; set; }
[JsonProperty("requestedBy")]
public LastChangedBy RequestedBy { get; set; }
[JsonProperty("lastChangedDate")]
public DateTimeOffset LastChangedDate { get; set; }
[JsonProperty("lastChangedBy")]
public LastChangedBy LastChangedBy { get; set; }
[JsonProperty("orchestrationPlan")]
public Plan OrchestrationPlan { get; set; }
[JsonProperty("logs")]
public Logs Logs { get; set; }
[JsonProperty("repository")]
public Repository Repository { get; set; }
[JsonProperty("keepForever")]
public bool KeepForever { get; set; }
[JsonProperty("retainedByRelease")]
public bool RetainedByRelease { get; set; }
[JsonProperty("triggeredByBuild")]
public object TriggeredByBuild { get; set; }
}
public class Definition
{
[JsonProperty("drafts")]
public List<object> Drafts { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("uri")]
public string Uri { get; set; }
[JsonProperty("path")]
public string Path { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("queueStatus")]
public string QueueStatus { get; set; }
[JsonProperty("revision")]
public long Revision { get; set; }
[JsonProperty("project")]
public Project Project { get; set; }
}
public class Project
{
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("state")]
public string State { get; set; }
[JsonProperty("revision")]
public long Revision { get; set; }
[JsonProperty("visibility")]
public string Visibility { get; set; }
[JsonProperty("lastUpdateTime")]
public DateTimeOffset LastUpdateTime { get; set; }
}
public class LastChangedBy
{
[JsonProperty("displayName")]
public string DisplayName { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("_links")]
public LastChangedByLinks Links { get; set; }
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("uniqueName")]
public object UniqueName { get; set; }
[JsonProperty("imageUrl")]
public object ImageUrl { get; set; }
[JsonProperty("descriptor")]
public string Descriptor { get; set; }
}
public class LastChangedByLinks
{
[JsonProperty("avatar")]
public Badge Avatar { get; set; }
}
public class Badge
{
[JsonProperty("href")]
public Uri Href { get; set; }
}
public class BuildLinks
{
[JsonProperty("self")]
public Badge Self { get; set; }
[JsonProperty("web")]
public Badge Web { get; set; }
[JsonProperty("sourceVersionDisplayUri")]
public Badge SourceVersionDisplayUri { get; set; }
[JsonProperty("timeline")]
public Badge Timeline { get; set; }
[JsonProperty("badge")]
public Badge Badge { get; set; }
}
public class Logs
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
}
public class Plan
{
[JsonProperty("planId")]
public Guid PlanId { get; set; }
}
public class Properties
{
}
public class Repository
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("clean")]
public object Clean { get; set; }
[JsonProperty("checkoutSubmodules")]
public bool CheckoutSubmodules { get; set; }
}
public class TriggerInfo
{
[JsonProperty("ci.sourceBranch")]
public string CiSourceBranch { get; set; }
[JsonProperty("ci.sourceSha")]
public string CiSourceSha { get; set; }
[JsonProperty("ci.message")]
public string CiMessage { get; set; }
[JsonProperty("ci.triggerRepository")]
public string CiTriggerRepository { get; set; }
}
}

View File

@ -0,0 +1,244 @@
#nullable disable
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Artemis.UI.Windows.Models
{
public class GitHubDifference
{
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("html_url")]
public Uri HtmlUrl { get; set; }
[JsonProperty("permalink_url")]
public Uri PermalinkUrl { get; set; }
[JsonProperty("diff_url")]
public Uri DiffUrl { get; set; }
[JsonProperty("patch_url")]
public Uri PatchUrl { get; set; }
[JsonProperty("base_commit")]
public BaseCommitClass BaseCommit { get; set; }
[JsonProperty("merge_base_commit")]
public BaseCommitClass MergeBaseCommit { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("ahead_by")]
public long AheadBy { get; set; }
[JsonProperty("behind_by")]
public long BehindBy { get; set; }
[JsonProperty("total_commits")]
public long TotalCommits { get; set; }
[JsonProperty("commits")]
public List<BaseCommitClass> Commits { get; set; }
[JsonProperty("files")]
public List<File> Files { get; set; }
}
public class BaseCommitClass
{
[JsonProperty("sha")]
public string Sha { get; set; }
[JsonProperty("node_id")]
public string NodeId { get; set; }
[JsonProperty("commit")]
public BaseCommitCommit Commit { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("html_url")]
public Uri HtmlUrl { get; set; }
[JsonProperty("comments_url")]
public Uri CommentsUrl { get; set; }
[JsonProperty("author")]
public BaseCommitAuthor Author { get; set; }
[JsonProperty("committer")]
public BaseCommitAuthor Committer { get; set; }
[JsonProperty("parents")]
public List<Parent> Parents { get; set; }
}
public class BaseCommitAuthor
{
[JsonProperty("login")]
public string Login { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("node_id")]
public string NodeId { get; set; }
[JsonProperty("avatar_url")]
public Uri AvatarUrl { get; set; }
[JsonProperty("gravatar_id")]
public string GravatarId { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("html_url")]
public Uri HtmlUrl { get; set; }
[JsonProperty("followers_url")]
public Uri FollowersUrl { get; set; }
[JsonProperty("following_url")]
public string FollowingUrl { get; set; }
[JsonProperty("gists_url")]
public string GistsUrl { get; set; }
[JsonProperty("starred_url")]
public string StarredUrl { get; set; }
[JsonProperty("subscriptions_url")]
public Uri SubscriptionsUrl { get; set; }
[JsonProperty("organizations_url")]
public Uri OrganizationsUrl { get; set; }
[JsonProperty("repos_url")]
public Uri ReposUrl { get; set; }
[JsonProperty("events_url")]
public string EventsUrl { get; set; }
[JsonProperty("received_events_url")]
public Uri ReceivedEventsUrl { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("site_admin")]
public bool SiteAdmin { get; set; }
}
public class BaseCommitCommit
{
[JsonProperty("author")]
public PurpleAuthor Author { get; set; }
[JsonProperty("committer")]
public PurpleAuthor Committer { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("tree")]
public Tree Tree { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("comment_count")]
public long CommentCount { get; set; }
[JsonProperty("verification")]
public Verification Verification { get; set; }
}
public class PurpleAuthor
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("date")]
public DateTimeOffset Date { get; set; }
}
public class Tree
{
[JsonProperty("sha")]
public string Sha { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
}
public class Verification
{
[JsonProperty("verified")]
public bool Verified { get; set; }
[JsonProperty("reason")]
public string Reason { get; set; }
[JsonProperty("signature")]
public string Signature { get; set; }
[JsonProperty("payload")]
public string Payload { get; set; }
}
public class Parent
{
[JsonProperty("sha")]
public string Sha { get; set; }
[JsonProperty("url")]
public Uri Url { get; set; }
[JsonProperty("html_url")]
public Uri HtmlUrl { get; set; }
}
public class File
{
[JsonProperty("sha")]
public string Sha { get; set; }
[JsonProperty("filename")]
public string Filename { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("additions")]
public long Additions { get; set; }
[JsonProperty("deletions")]
public long Deletions { get; set; }
[JsonProperty("changes")]
public long Changes { get; set; }
[JsonProperty("blob_url")]
public Uri BlobUrl { get; set; }
[JsonProperty("raw_url")]
public Uri RawUrl { get; set; }
[JsonProperty("contents_url")]
public Uri ContentsUrl { get; set; }
[JsonProperty("patch")]
public string Patch { get; set; }
[JsonProperty("previous_filename", NullValueHandling = NullValueHandling.Ignore)]
public string PreviousFilename { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using Artemis.Core.Services;
using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Windows.Providers;
using Ninject.Modules;
@ -14,6 +15,8 @@ public class WindowsModule : NinjectModule
{
Kernel!.Bind<ICursorProvider>().To<CursorProvider>().InSingletonScope();
Kernel!.Bind<IGraphicsContextProvider>().To<GraphicsContextProvider>().InSingletonScope();
Kernel!.Bind<IUpdateProvider>().To<UpdateProvider>().InSingletonScope();
Kernel!.Bind<IAutoRunProvider>().To<AutoRunProvider>();
}
#endregion

View File

@ -0,0 +1,126 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Artemis.Core;
using Artemis.UI.Shared.Providers;
using Avalonia.Platform;
namespace Artemis.UI.Windows.Providers;
public class AutoRunProvider : IAutoRunProvider
{
private readonly IAssetLoader _assetLoader;
public AutoRunProvider(IAssetLoader assetLoader)
{
_assetLoader = assetLoader;
}
private async Task<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();
await schtasks.WaitForExitAsync();
return schtasks.ExitCode == 0;
}
private async Task CreateAutoRunTask(TimeSpan autoRunDelay)
{
await using Stream taskFile = _assetLoader.Open(new Uri("avares://Artemis 2/Assets/autorun.xml"));
XDocument document = await XDocument.LoadAsync(taskFile, LoadOptions.None, CancellationToken.None);
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();
await using (Stream fileStream = new FileStream(xmlPath, FileMode.Create))
await document.SaveAsync(fileStream, SaveOptions.None, CancellationToken.None);
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();
await schtasks.WaitForExitAsync();
File.Delete(xmlPath);
}
private async Task 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();
await schtasks.WaitForExitAsync();
}
/// <inheritdoc />
public async Task EnableAutoRun(bool recreate, int autoRunDelay)
{
// if (Constants.BuildInfo.IsLocalBuild)
// return;
// Create or remove the task if necessary
bool taskCreated = false;
if (!recreate)
taskCreated = await IsAutoRunTaskCreated();
if (!taskCreated)
await CreateAutoRunTask(TimeSpan.FromSeconds(autoRunDelay));
}
/// <inheritdoc />
public async Task DisableAutoRun()
{
bool taskCreated = await IsAutoRunTaskCreated();
if (taskCreated)
await RemoveAutoRunTask();
}
}

View File

@ -11,9 +11,9 @@ public class CursorProvider : ICursorProvider
{
public CursorProvider(IAssetLoader assetLoader)
{
Rotate = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_rotate.png"))), new PixelPoint(21, 10));
Drag = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag.png"))), new PixelPoint(11, 3));
DragHorizontal = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag_horizontal.png"))), new PixelPoint(16, 5));
Rotate = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis 2/Assets/Cursors/aero_rotate.png"))), new PixelPoint(21, 10));
Drag = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis 2/Assets/Cursors/aero_drag.png"))), new PixelPoint(11, 3));
DragHorizontal = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis 2/Assets/Cursors/aero_drag_horizontal.png"))), new PixelPoint(16, 5));
}
public Cursor Rotate { get; }

View File

@ -1,5 +1,4 @@
using System.Collections.Generic;
using Artemis.Core.Services;
using Artemis.Core.Providers;
using Artemis.Core.SkiaSharp;
using Artemis.UI.Windows.SkiaSharp;
@ -9,18 +8,11 @@ public class GraphicsContextProvider : IGraphicsContextProvider
{
private VulkanContext? _vulkanContext;
/// <inheritdoc />
public IReadOnlyCollection<string> GraphicsContextNames => new List<string> {"Vulkan"}.AsReadOnly();
public string GraphicsContextName => "Vulkan";
/// <inheritdoc />
public IManagedGraphicsContext? GetGraphicsContext(string name)
public IManagedGraphicsContext? GetGraphicsContext()
{
if (name == "Vulkan")
{
_vulkanContext ??= new VulkanContext();
return _vulkanContext;
}
return null;
_vulkanContext ??= new VulkanContext();
return _vulkanContext;
}
}

View File

@ -0,0 +1,216 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Artemis.UI.Exceptions;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.MainWindow;
using Artemis.UI.Windows.Models;
using Artemis.UI.Windows.Screens.Update;
using Avalonia.Threading;
using Flurl;
using Flurl.Http;
using Microsoft.Toolkit.Uwp.Notifications;
using Serilog;
using File = System.IO.File;
namespace Artemis.UI.Windows.Providers;
public class UpdateProvider : IUpdateProvider, IDisposable
{
private const string ApiUrl = "https://dev.azure.com/artemis-rgb/Artemis/_apis/";
private const string InstallerUrl = "https://builds.artemis-rgb.com/binaries/Artemis.Installer.exe";
private readonly ILogger _logger;
private readonly IWindowService _windowService;
private readonly IMainWindowService _mainWindowService;
public UpdateProvider(ILogger logger, IWindowService windowService, IMainWindowService mainWindowService)
{
_logger = logger;
_windowService = windowService;
_mainWindowService = mainWindowService;
ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompatOnOnActivated;
}
private async void ToastNotificationManagerCompatOnOnActivated(ToastNotificationActivatedEventArgsCompat e)
{
ToastArguments args = ToastArguments.Parse(e.Argument);
string channel = args.Get("channel");
string action = "view-changes";
if (args.Contains("action"))
action = args.Get("action");
if (action == "install")
await RunInstaller(channel, true);
else if (action == "view-changes")
{
await Dispatcher.UIThread.InvokeAsync(async () =>
{
_mainWindowService.OpenMainWindow();
await OfferUpdate(channel, true);
});
}
}
private async Task RunInstaller(string channel, bool silent)
{
_logger.Information("ApplyUpdate: Applying update");
// Ensure the installer is up-to-date, get installer build info
DevOpsBuild? buildInfo = await GetBuildInfo(6);
string installerPath = Path.Combine(Core.Constants.DataFolder, "installer", "Artemis.Installer.exe");
// Always update installer if it is missing ^^
if (!File.Exists(installerPath))
await UpdateInstaller();
// Compare the creation date of the installer with the build date and update if needed
else
{
if (buildInfo != null && File.GetLastWriteTime(installerPath) < buildInfo.FinishTime)
await UpdateInstaller();
}
_logger.Information("ApplyUpdate: Running installer at {InstallerPath}", installerPath);
try
{
Process.Start(new ProcessStartInfo(installerPath, "-autoupdate")
{
UseShellExecute = true,
Verb = "runas"
});
}
catch (Win32Exception e)
{
if (e.NativeErrorCode == 0x4c7)
_logger.Warning("ApplyUpdate: Operation was cancelled, user likely clicked No in UAC dialog");
else
throw;
}
}
private async Task UpdateInstaller()
{
string installerDirectory = Path.Combine(Core.Constants.DataFolder, "installer");
string installerPath = Path.Combine(installerDirectory, "Artemis.Installer.exe");
_logger.Information("UpdateInstaller: Downloading installer from {DownloadUrl}", InstallerUrl);
using HttpClient client = new();
HttpResponseMessage httpResponseMessage = await client.GetAsync(InstallerUrl);
if (!httpResponseMessage.IsSuccessStatusCode)
throw new ArtemisUIException($"Failed to download installer, status code {httpResponseMessage.StatusCode}");
_logger.Information("UpdateInstaller: Writing installer file to {InstallerPath}", installerPath);
if (File.Exists(installerPath))
File.Delete(installerPath);
Core.Utilities.CreateAccessibleDirectory(installerDirectory);
await using FileStream fs = new(installerPath, FileMode.Create, FileAccess.Write, FileShare.None);
await httpResponseMessage.Content.CopyToAsync(fs);
}
/// <inheritdoc />
public async Task<bool> CheckForUpdate(string channel)
{
DevOpsBuild? buildInfo = await GetBuildInfo(1);
if (buildInfo == null)
return false;
double buildNumber = double.Parse(buildInfo.BuildNumber, CultureInfo.InvariantCulture);
string buildNumberDisplay = buildNumber.ToString(CultureInfo.InvariantCulture);
_logger.Information("Latest build is {BuildNumber}, we're running {LocalBuildNumber}", buildNumberDisplay, Core.Constants.BuildInfo.BuildNumberDisplay);
return buildNumber > Core.Constants.BuildInfo.BuildNumber;
}
/// <inheritdoc />
public async Task ApplyUpdate(string channel, bool silent)
{
await RunInstaller(channel, silent);
}
/// <inheritdoc />
public async Task OfferUpdate(string channel, bool windowOpen)
{
if (windowOpen)
{
bool update = await _windowService.ShowDialogAsync<UpdateDialogViewModel, bool>(("channel", channel));
if (update)
await RunInstaller(channel, false);
}
else
{
ShowDesktopNotification(channel);
}
}
private void ShowDesktopNotification(string channel)
{
new ToastContentBuilder()
.AddArgument("channel", channel)
.AddText("An update is available")
.AddButton(new ToastButton().SetContent("Install").AddArgument("action", "install").SetBackgroundActivation())
.AddButton(new ToastButton().SetContent("View changes").AddArgument("action", "view-changes"))
.AddHeroImage(new Uri(@"C:\Repos\Artemis\src\Artemis.UI\Assets\Images\home-banner.png"))
.Show();
}
public async Task<DevOpsBuild?> GetBuildInfo(int buildDefinition, string? buildNumber = null)
{
Url request = ApiUrl.AppendPathSegments("build", "builds")
.SetQueryParam("definitions", buildDefinition)
.SetQueryParam("resultFilter", "succeeded")
.SetQueryParam("$top", 1)
.SetQueryParam("api-version", "6.1-preview.6");
if (buildNumber != null)
request = request.SetQueryParam("buildNumber", buildNumber);
try
{
DevOpsBuilds result = await request.GetJsonAsync<DevOpsBuilds>();
try
{
return result.Builds.FirstOrDefault();
}
catch (Exception e)
{
_logger.Warning(e, "GetBuildInfo: Failed to retrieve build info JSON");
throw;
}
}
catch (FlurlHttpException e)
{
_logger.Warning("GetBuildInfo: Getting build info, request returned {StatusCode}", e.StatusCode);
throw;
}
}
public async Task<GitHubDifference> GetBuildDifferences(DevOpsBuild a, DevOpsBuild b)
{
return await "https://api.github.com"
.AppendPathSegments("repos", "Artemis-RGB", "Artemis", "compare")
.AppendPathSegment(a.SourceVersion + "..." + b.SourceVersion)
.WithHeader("User-Agent", "Artemis 2")
.WithHeader("Accept", "application/vnd.github.v3+json")
.GetJsonAsync<GitHubDifference>();
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
ToastNotificationManagerCompat.OnActivated -= ToastNotificationManagerCompatOnOnActivated;
ToastNotificationManagerCompat.Uninstall();
}
#endregion
}

View File

@ -0,0 +1,70 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:update="clr-namespace:Artemis.UI.Windows.Screens.Update"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Windows.Screens.Update.UpdateDialogView"
x:DataType="update:UpdateDialogViewModel"
Title="Artemis | Update available"
Width="750"
MinWidth="750"
Height="500"
WindowStartupLocation="CenterOwner">
<Grid Margin="15" RowDefinitions="Auto,*,Auto">
<TextBlock Grid.Row="0" Classes="h4">
A new Artemis update is available! 🥳
</TextBlock>
<StackPanel Grid.Row="1" IsVisible="{CompiledBinding RetrievingChanges}" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock>Retrieving changes...</TextBlock>
<ProgressBar IsIndeterminate="True"></ProgressBar>
</StackPanel>
<Border Grid.Row="1" Classes="card" IsVisible="{CompiledBinding !RetrievingChanges}">
<Grid RowDefinitions="Auto,*">
<StackPanel Grid.Row="0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="You are currently running build " />
<TextBlock Text="{CompiledBinding CurrentBuild, Mode=OneWay}"></TextBlock>
<TextBlock Text=" while the latest build is " />
<TextBlock Text="{CompiledBinding LatestBuild, Mode=OneWay}"></TextBlock>
<TextBlock Text="." />
</StackPanel>
<TextBlock Text="Updating Artemis will give you the latest bug(fixes), features and improvements." />
<Separator Classes="card-separator" />
<TextBlock Classes="h5" IsVisible="{CompiledBinding HasChanges}">
Changelog (auto-generated)
</TextBlock>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalAlignment="Top" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" IsVisible="{CompiledBinding HasChanges}">
<ItemsControl Items="{CompiledBinding Changes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="Auto,*">
<TextBlock Text="•" Margin="10 0" />
<TextBlock Grid.Column="1" Text="{Binding}" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<StackPanel Grid.Row="1" IsVisible="{CompiledBinding !HasChanges}" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center">We couldn't retrieve any changes</TextBlock>
<controls:HyperlinkButton NavigateUri="https://github.com/Artemis-RGB/Artemis/commits/master" HorizontalAlignment="Center">View online</controls:HyperlinkButton>
</StackPanel>
</Grid>
</Border>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Spacing="5" Grid.Row="2" Margin="0 15 0 0">
<Button Classes="accent" Command="{CompiledBinding Install}">Install update</Button>
<Button Command="{CompiledBinding AskLater}">Ask later</Button>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,22 @@
using Artemis.UI.Shared;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Windows.Screens.Update;
public partial class UpdateDialogView : ReactiveCoreWindow<UpdateDialogViewModel>
{
public UpdateDialogView()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

View File

@ -0,0 +1,119 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Windows.Models;
using Artemis.UI.Windows.Providers;
using Avalonia.Threading;
using DynamicData;
using ReactiveUI;
namespace Artemis.UI.Windows.Screens.Update;
public class UpdateDialogViewModel : DialogViewModelBase<bool>
{
private readonly UpdateProvider _updateProvider;
private readonly INotificationService _notificationService;
// Based on https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#skipping-ci-for-individual-commits
private readonly string[] _excludedCommitMessages =
{
"[skip ci]",
"[ci skip]",
"skip-checks: true",
"skip-checks:true",
"[skip azurepipelines]",
"[azurepipelines skip]",
"[skip azpipelines]",
"[azpipelines skip]",
"[skip azp]",
"[azp skip]",
"***NO_CI***"
};
private bool _retrievingChanges;
private bool _hasChanges;
private string? _latestBuild;
public UpdateDialogViewModel(string channel, IUpdateProvider updateProvider, INotificationService notificationService)
{
_updateProvider = (UpdateProvider) updateProvider;
_notificationService = notificationService;
Channel = channel;
CurrentBuild = Core.Constants.BuildInfo.BuildNumberDisplay;
this.WhenActivated((CompositeDisposable _) => Dispatcher.UIThread.InvokeAsync(GetBuildChanges));
Install = ReactiveCommand.Create(() => Close(true));
AskLater = ReactiveCommand.Create(() => Close(false));
}
public ReactiveCommand<Unit, Unit> Install { get; }
public ReactiveCommand<Unit, Unit> AskLater { get; }
public string Channel { get; }
public string CurrentBuild { get; }
public ObservableCollection<string> Changes { get; } = new();
public bool RetrievingChanges
{
get => _retrievingChanges;
set => RaiseAndSetIfChanged(ref _retrievingChanges, value);
}
public bool HasChanges
{
get => _hasChanges;
set => RaiseAndSetIfChanged(ref _hasChanges, value);
}
public string? LatestBuild
{
get => _latestBuild;
set => RaiseAndSetIfChanged(ref _latestBuild, value);
}
private async Task GetBuildChanges()
{
try
{
RetrievingChanges = true;
Task<DevOpsBuild?> currentTask = _updateProvider.GetBuildInfo(1, CurrentBuild);
Task<DevOpsBuild?> latestTask = _updateProvider.GetBuildInfo(1);
DevOpsBuild? current = await currentTask;
DevOpsBuild? latest = await latestTask;
LatestBuild = latest?.BuildNumber;
if (current != null && latest != null)
{
GitHubDifference difference = await _updateProvider.GetBuildDifferences(current, latest);
// Only take commits with one parents (no merges)
Changes.Clear();
Changes.AddRange(difference.Commits.Where(c => c.Parents.Count == 1)
.SelectMany(c => c.Commit.Message.Split("\n"))
.Select(m => m.Trim())
.Where(m => !string.IsNullOrWhiteSpace(m) && !_excludedCommitMessages.Contains(m))
.OrderBy(m => m)
);
HasChanges = Changes.Any();
}
}
catch (Exception e)
{
_notificationService.CreateNotification().WithTitle("Failed to retrieve build changes").WithMessage(e.Message).WithSeverity(NotificationSeverity.Error).Show();
}
finally
{
RetrievingChanges = false;
}
}
}

View File

@ -1,7 +1,7 @@
{
"version": 1,
"dependencies": {
"net6.0-windows7.0": {
"net6.0-windows10.0.17763": {
"Avalonia": {
"type": "Direct",
"requested": "[0.10.15, )",
@ -65,6 +65,18 @@
"System.Numerics.Vectors": "4.5.0"
}
},
"Microsoft.Toolkit.Uwp.Notifications": {
"type": "Direct",
"requested": "[7.1.2, )",
"resolved": "7.1.2",
"contentHash": "cVBRDG8g0K4sBRHFJoNQjwZduDUz+cXP33erIQjAa8fGKF4DsWIuOAQCYGFoXpdXIXjxoaUYuGSxAG6QCvOdtQ==",
"dependencies": {
"Microsoft.Win32.Registry": "4.7.0",
"System.Drawing.Common": "4.7.0",
"System.Reflection.Emit": "4.7.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.Win32": {
"type": "Direct",
"requested": "[2.0.1, )",
@ -461,12 +473,21 @@
"System.Runtime": "4.3.0"
}
},
"Microsoft.Win32.Registry": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==",
"dependencies": {
"System.Security.AccessControl": "4.7.0",
"System.Security.Principal.Windows": "4.7.0"
}
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "LuI1oG+24TUj1ZRQQjM5Ew73BKnZE5NZ/7eAdh1o8ST5dPhUnJvIkiIn2re3MwnkRy6ELRnvEbBxHP8uALKhJw==",
"resolved": "4.7.0",
"contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==",
"dependencies": {
"Microsoft.NETCore.Platforms": "2.0.0"
"Microsoft.NETCore.Platforms": "3.1.0"
}
},
"NETStandard.Library": {
@ -977,11 +998,11 @@
},
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "AiJFxxVPdeITstiRS5aAu8+8Dpf5NawTMoapZ53Gfirml24p7HIfhjmCRxdXnmmf3IUA3AX3CcW7G73CjWxW/Q==",
"resolved": "4.7.0",
"contentHash": "v+XbyYHaZjDfn0ENmJEV1VYLgGgCTx1gnfOBcppowbpOAriglYgGCvFCPr2EEZyBvXlpxbEsTwkOlInl107ahA==",
"dependencies": {
"Microsoft.NETCore.Platforms": "2.0.0",
"Microsoft.Win32.SystemEvents": "4.5.0"
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.Win32.SystemEvents": "4.7.0"
}
},
"System.Dynamic.Runtime": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

View File

@ -21,14 +21,15 @@ namespace Artemis.UI.Screens.Root
{
public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvider
{
private readonly IAssetLoader _assetLoader;
private readonly DefaultTitleBarViewModel _defaultTitleBarViewModel;
private readonly ICoreService _coreService;
private readonly IDebugService _debugService;
private readonly IClassicDesktopStyleApplicationLifetime _lifeTime;
private readonly ISettingsService _settingsService;
private readonly ISidebarVmFactory _sidebarVmFactory;
private readonly IWindowService _windowService;
private readonly IDebugService _debugService;
private readonly IUpdateService _updateService;
private readonly IAssetLoader _assetLoader;
private readonly IClassicDesktopStyleApplicationLifetime _lifeTime;
private readonly ISidebarVmFactory _sidebarVmFactory;
private SidebarViewModel? _sidebarViewModel;
private ViewModelBase? _titleBarViewModel;
@ -38,6 +39,7 @@ namespace Artemis.UI.Screens.Root
IWindowService windowService,
IMainWindowService mainWindowService,
IDebugService debugService,
IUpdateService updateService,
IAssetLoader assetLoader,
DefaultTitleBarViewModel defaultTitleBarViewModel,
ISidebarVmFactory sidebarVmFactory)
@ -48,6 +50,7 @@ namespace Artemis.UI.Screens.Root
_settingsService = settingsService;
_windowService = windowService;
_debugService = debugService;
_updateService = updateService;
_assetLoader = assetLoader;
_defaultTitleBarViewModel = defaultTitleBarViewModel;
_sidebarVmFactory = sidebarVmFactory;

View File

@ -6,6 +6,8 @@
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:behaviors="clr-namespace:Artemis.UI.Shared.Behaviors;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="2400"
x:Class="Artemis.UI.Screens.Settings.GeneralTabView"
x:DataType="settings:GeneralTabViewModel">
@ -18,40 +20,46 @@
</TextBlock>
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
<StackPanel>
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>Auto-run on startup</TextBlock>
</StackPanel>
<ToggleSwitch Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" IsChecked="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
</Grid>
<Separator Classes="card-separator" />
<StackPanel IsVisible="{CompiledBinding IsAutoRunSupported}">
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>Auto-run on startup</TextBlock>
</StackPanel>
<ToggleSwitch Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" IsChecked="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
</Grid>
<Separator Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>Hide window on auto-run</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleSwitch IsChecked="{CompiledBinding !UIShowOnStartup.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
</StackPanel>
</Grid>
<Separator Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>Hide window on auto-run</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleSwitch IsChecked="{CompiledBinding !UIShowOnStartup.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
</StackPanel>
</Grid>
<Separator Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>Startup delay</TextBlock>
<TextBlock Classes="subtitle" TextWrapping="Wrap">
Set the amount of seconds to wait before auto-running Artemis.
</TextBlock>
<TextBlock Classes="subtitle" TextWrapping="Wrap">
If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
<TextBox Text="{CompiledBinding UIAutoRunDelay.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120" />
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
</StackPanel>
</Grid>
<Separator Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>Startup delay</TextBlock>
<TextBlock Classes="subtitle" TextWrapping="Wrap">
Set the amount of seconds to wait before auto-running Artemis.
</TextBlock>
<TextBlock Classes="subtitle" TextWrapping="Wrap">
If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
<controls:NumberBox IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120">
<Interaction.Behaviors>
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding UIAutoRunDelay.Value}"/>
</Interaction.Behaviors>
</controls:NumberBox>
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
</StackPanel>
</Grid>
<Separator Classes="card-separator" />
</StackPanel>
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
@ -103,48 +111,69 @@
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<TextBox Text="{CompiledBinding WebServerPort.Value}" Width="150" />
<controls:NumberBox Width="150">
<Interaction.Behaviors>
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding WebServerPort.Value}"/>
</Interaction.Behaviors>
</controls:NumberBox>
</StackPanel>
</Grid>
</StackPanel>
</Border>
<!-- Update settings -->
<TextBlock Classes="h4" Margin="0 15">
Updating
</TextBlock>
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
<StackPanel>
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>
Check for updates
</TextBlock>
<TextBlock Classes="subtitle" TextWrapping="Wrap">
If enabled, we'll check for updates on startup and periodically while running.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" />
</StackPanel>
</Grid>
<Separator Classes="card-separator" />
<StackPanel IsVisible="{CompiledBinding IsUpdatingSupported}">
<TextBlock Classes="h4" Margin="0 15">
Updating
</TextBlock>
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
<StackPanel>
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>
Check for updates
</TextBlock>
<TextBlock Classes="subtitle" TextWrapping="Wrap">
If enabled, we'll check for updates on startup and periodically while running.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" />
</StackPanel>
</Grid>
<Separator Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock>
Auto-install updates
</TextBlock>
<TextBlock Classes="subtitle" TextWrapping="Wrap">
If enabled, new updates will automatically be installed.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleSwitch IsEnabled="{CompiledBinding UICheckForUpdates.Value}" IsChecked="{CompiledBinding UIAutoUpdate.Value}" MinWidth="0" />
</StackPanel>
</Grid>
<Separator Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock>
Update
</TextBlock>
<TextBlock Classes="subtitle">
Use the button on the right to check for updates now.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<Button Command="{Binding OfferUpdatesIfFound}" Width="150" Content="Check now" />
</StackPanel>
</Grid>
</StackPanel>
</Border>
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock>
Update
</TextBlock>
<TextBlock Classes="subtitle">
Use the button on the right to check for updates now.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<Button Command="{CompiledBinding CheckForUpdate}" Width="150" Content="Check now" />
</StackPanel>
</Grid>
</StackPanel>
</Border>
</StackPanel>
<!-- Profile editor settings -->
<TextBlock Classes="h4" Margin="0 15">

View File

@ -9,14 +9,15 @@ using System.Threading;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.LayerBrushes;
using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.UI.Screens.StartupWizard;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services;
using Avalonia;
using Avalonia.Threading;
using DynamicData;
using FluentAvalonia.Styling;
using Ninject;
using ReactiveUI;
using Serilog.Events;
@ -29,20 +30,28 @@ namespace Artemis.UI.Screens.Settings
private readonly ISettingsService _settingsService;
private readonly IDebugService _debugService;
private readonly IWindowService _windowService;
private readonly IUpdateService _updateService;
private readonly IAutoRunProvider? _autoRunProvider;
public GeneralTabViewModel(IKernel kernel, ISettingsService settingsService, IPluginManagementService pluginManagementService, IDebugService debugService, IWindowService windowService)
public GeneralTabViewModel(IKernel kernel,
ISettingsService settingsService,
IPluginManagementService pluginManagementService,
IDebugService debugService,
IWindowService windowService,
IUpdateService updateService)
{
DisplayName = "General";
_settingsService = settingsService;
_debugService = debugService;
_windowService = windowService;
_updateService = updateService;
_autoRunProvider = kernel.TryGet<IAutoRunProvider>();
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();
List<IGraphicsContextProvider> graphicsContextProviders = kernel.Get<List<IGraphicsContextProvider>>();
LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
GraphicsContexts = new ObservableCollection<string> {"Software"};
IGraphicsContextProvider? graphicsContextProvider = kernel.TryGet<IGraphicsContextProvider>();
if (graphicsContextProvider != null)
GraphicsContexts.AddRange(graphicsContextProvider.GraphicsContextNames);
GraphicsContexts.AddRange(graphicsContextProviders.Select(p => p.GraphicsContextName));
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
{
@ -55,6 +64,21 @@ namespace Artemis.UI.Screens.Settings
ShowSetupWizard = ReactiveCommand.CreateFromTask(ExecuteShowSetupWizard);
ShowDebugger = ReactiveCommand.Create(ExecuteShowDebugger);
ShowDataFolder = ReactiveCommand.Create(ExecuteShowDataFolder);
this.WhenActivated(d =>
{
UIAutoRun.SettingChanged += UIAutoRunOnSettingChanged;
UIAutoRunDelay.SettingChanged += UIAutoRunDelayOnSettingChanged;
Dispatcher.UIThread.InvokeAsync(ApplyAutoRun);
Disposable.Create(() =>
{
UIAutoRun.SettingChanged -= UIAutoRunOnSettingChanged;
UIAutoRunDelay.SettingChanged -= UIAutoRunDelayOnSettingChanged;
_settingsService.SaveAllSettings();
}).DisposeWith(d);
});
}
public ReactiveCommand<Unit, Unit> ShowLogs { get; }
@ -63,6 +87,9 @@ namespace Artemis.UI.Screens.Settings
public ReactiveCommand<Unit, Unit> ShowDebugger { get; }
public ReactiveCommand<Unit, Unit> ShowDataFolder { get; }
public bool IsAutoRunSupported => _autoRunProvider != null;
public bool IsUpdatingSupported => _updateService.UpdatingSupported;
public ObservableCollection<LayerBrushDescriptor> LayerBrushDescriptors { get; }
public ObservableCollection<string> GraphicsContexts { get; }
@ -116,6 +143,7 @@ namespace Artemis.UI.Screens.Settings
public PluginSetting<int> UIAutoRunDelay => _settingsService.GetSetting("UI.AutoRunDelay", 15);
public PluginSetting<bool> UIShowOnStartup => _settingsService.GetSetting("UI.ShowOnStartup", true);
public PluginSetting<bool> UICheckForUpdates => _settingsService.GetSetting("UI.CheckForUpdates", true);
public PluginSetting<bool> UIAutoUpdate => _settingsService.GetSetting("UI.AutoUpdate", false);
public PluginSetting<bool> ProfileEditorShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
public PluginSetting<LogEventLevel> CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information);
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
@ -123,26 +151,17 @@ namespace Artemis.UI.Screens.Settings
public PluginSetting<int> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30);
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);
#region General
private void ExecuteShowLogs()
{
OpenFolder(Constants.LogsFolder);
Utilities.OpenFolder(Constants.LogsFolder);
}
#endregion
#region Updating
private Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
private async Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
{
return Task.CompletedTask;
await _updateService.ManualUpdate();
}
#endregion
#region Tools
private async Task ExecuteShowSetupWizard()
{
await _windowService.ShowDialogAsync<StartupWizardViewModel, bool>();
@ -153,19 +172,46 @@ namespace Artemis.UI.Screens.Settings
_debugService.ShowDebugger();
}
private void ExecuteShowDataFolder()
{
OpenFolder(Constants.DataFolder);
Utilities.OpenFolder(Constants.DataFolder);
}
#endregion
private void OpenFolder(string path)
private async Task ApplyAutoRun()
{
if (OperatingSystem.IsWindows())
if (_autoRunProvider == null)
return;
try
{
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", path);
if (UIAutoRun.Value)
await _autoRunProvider.EnableAutoRun(false, UIAutoRunDelay.Value);
else
await _autoRunProvider.DisableAutoRun();
}
catch (Exception exception)
{
_windowService.ShowExceptionDialog("Failed to apply auto-run", exception);
}
}
private async void UIAutoRunOnSettingChanged(object? sender, EventArgs e)
{
await ApplyAutoRun();
}
private async void UIAutoRunDelayOnSettingChanged(object? sender, EventArgs e)
{
if (_autoRunProvider == null || !UIAutoRun.Value)
return;
try
{
await _autoRunProvider.EnableAutoRun(true, UIAutoRunDelay.Value);
}
catch (Exception exception)
{
_windowService.ShowExceptionDialog("Failed to apply auto-run", exception);
}
}
}

View File

@ -0,0 +1,22 @@
using System.Threading.Tasks;
namespace Artemis.UI.Services.Interfaces;
public interface IUpdateService : IArtemisUIService
{
/// <summary>
/// Gets a boolean indicating whether updating is supported.
/// </summary>
bool UpdatingSupported { get; }
/// <summary>
/// Gets or sets a boolean indicating whether auto-updating is suspended.
/// </summary>
bool SuspendAutoUpdate { get; set; }
/// <summary>
/// Manually checks for updates and offers to install it if found.
/// </summary>
/// <returns>Whether an update was found, regardless of whether the user chose to install it.</returns>
Task ManualUpdate();
}

View File

@ -0,0 +1,113 @@
using System;
using System.Threading.Tasks;
using System.Timers;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services.MainWindow;
using Avalonia.Threading;
using Ninject;
using Serilog;
namespace Artemis.UI.Services;
public class UpdateService : IUpdateService
{
private const double UPDATE_CHECK_INTERVAL = 10000; // once per hour
private readonly PluginSetting<bool> _autoUpdate;
private readonly PluginSetting<bool> _checkForUpdates;
private readonly ILogger _logger;
private readonly IMainWindowService _mainWindowService;
private readonly IUpdateProvider? _updateProvider;
public UpdateService(ILogger logger, IKernel kernel, ISettingsService settingsService, IMainWindowService mainWindowService)
{
_logger = logger;
_mainWindowService = mainWindowService;
if (!Constants.BuildInfo.IsLocalBuild)
_updateProvider = kernel.TryGet<IUpdateProvider>();
_checkForUpdates = settingsService.GetSetting("UI.CheckForUpdates", true);
_autoUpdate = settingsService.GetSetting("UI.AutoUpdate", false);
_checkForUpdates.SettingChanged += CheckForUpdatesOnSettingChanged;
_mainWindowService.MainWindowOpened += WindowServiceOnMainWindowOpened;
Timer timer = new(UPDATE_CHECK_INTERVAL);
timer.Elapsed += TimerOnElapsed;
timer.Start();
}
private async void TimerOnElapsed(object? sender, ElapsedEventArgs e)
{
await AutoUpdate();
}
private async void CheckForUpdatesOnSettingChanged(object? sender, EventArgs e)
{
// Run an auto-update as soon as the setting gets changed to enabled
if (_checkForUpdates.Value)
await AutoUpdate();
}
private async void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
{
// await AutoUpdate();
}
private async Task AutoUpdate()
{
if (_updateProvider == null || !_checkForUpdates.Value || SuspendAutoUpdate)
return;
try
{
bool updateAvailable = await _updateProvider.CheckForUpdate("master");
if (!updateAvailable)
return;
// Only offer it once per session
SuspendAutoUpdate = true;
// If the window is open show the changelog, don't auto-update while the user is busy
if (_mainWindowService.IsMainWindowOpen)
{
await Dispatcher.UIThread.InvokeAsync(async () =>
{
// Call OpenMainWindow anyway to focus the main window
_mainWindowService.OpenMainWindow();
await _updateProvider.OfferUpdate("master", true);
});
return;
}
// If the window is closed but auto-update is enabled, update silently
if (_autoUpdate.Value)
await _updateProvider.ApplyUpdate("master", true);
// If auto-update is disabled the update provider can show a notification and handle the rest
else
await _updateProvider.OfferUpdate("master", false);
}
catch (Exception e)
{
_logger.Warning(e, "Auto update failed");
}
}
public bool SuspendAutoUpdate { get; set; }
public bool UpdatingSupported => _updateProvider != null;
public async Task ManualUpdate()
{
if (_updateProvider == null || !_mainWindowService.IsMainWindowOpen)
return;
bool updateAvailable = await _updateProvider.CheckForUpdate("master");
if (!updateAvailable)
return;
await _updateProvider.OfferUpdate("master", true);
}
}