[#8] Add auto update checker
This commit is contained in:
@ -24,6 +24,9 @@ namespace Elwig {
|
||||
|
||||
protected static App CurrentApp;
|
||||
public static int NumWindows => CurrentApp.Windows.Count;
|
||||
public static bool ForceShutdown { get; private set; } = false;
|
||||
|
||||
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
|
||||
|
||||
public static readonly string DataPath = @"C:\ProgramData\Elwig\";
|
||||
public static readonly string ExePath = @"C:\Program Files\Elwig\";
|
||||
@ -111,10 +114,26 @@ namespace Elwig {
|
||||
BranchNum = branches.Count;
|
||||
}
|
||||
|
||||
Utils.RunBackground("Temp File Cleanup", () => {
|
||||
Utils.CleanupTempFiles();
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
Utils.RunBackground("HTML Initialization", () => Html.Init(PrintingReadyChanged));
|
||||
Utils.RunBackground("PDF Initialization", () => Pdf.Init(PrintingReadyChanged));
|
||||
Utils.RunBackground("JSON Schema Initialization", BillingData.Init);
|
||||
|
||||
if (Config.UpdateAuto && Config.UpdateUrl != null) {
|
||||
if (Utils.HasInternetConnectivity()) {
|
||||
Utils.RunBackground("Auto Updater", async () => {
|
||||
await Task.Delay(500);
|
||||
await CheckForUpdates();
|
||||
});
|
||||
}
|
||||
_autoUpdateTimer.Tick += new EventHandler(OnAutoUpdateTimer);
|
||||
_autoUpdateTimer.Start();
|
||||
}
|
||||
|
||||
var list = new List<IScale>();
|
||||
foreach (var s in Config.Scales) {
|
||||
try {
|
||||
@ -184,6 +203,29 @@ namespace Elwig {
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAutoUpdateTimer(object? sender, EventArgs? evt) {
|
||||
foreach (Window w in CurrentApp.Windows) {
|
||||
if (w is UpdateDialog) return;
|
||||
}
|
||||
if (Utils.HasInternetConnectivity()) {
|
||||
Utils.RunBackground("Auto Updater", CheckForUpdates);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task CheckForUpdates() {
|
||||
if (Config.UpdateUrl == null) return;
|
||||
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
||||
if (latest != null && latest.Value.Version != Version) {
|
||||
await MainDispatcher.BeginInvoke(() => {
|
||||
var d = new UpdateDialog(latest.Value.Version, latest.Value.Url, latest.Value.Size);
|
||||
if (d.ShowDialog() == true) {
|
||||
ForceShutdown = true;
|
||||
Current.Shutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window {
|
||||
foreach (Window w in CurrentApp.Windows) {
|
||||
if (w is T t && (selector == null || selector(t))) {
|
||||
|
33
Elwig/Dialogs/UpdateDialog.xaml
Normal file
33
Elwig/Dialogs/UpdateDialog.xaml
Normal file
@ -0,0 +1,33 @@
|
||||
<Window x:Class="Elwig.Dialogs.UpdateDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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"
|
||||
mc:Ignorable="d"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Title="Neues Update verfügbar - Elwig" Height="180" Width="400">
|
||||
<Grid>
|
||||
<TextBlock x:Name="Description" FontSize="14" Margin="0,0,0,30"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center">
|
||||
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> von Elwig ist verfügbar!<LineBreak/>
|
||||
Soll das Update heruntergeladen und<LineBreak/>
|
||||
installiert werden? (ca. <Run x:Name="SizeText">100</Run> MB)<LineBreak/>
|
||||
<Run FontWeight="Bold">Achtung</Run>: Elwig wird dabei geschlossen!
|
||||
</TextBlock>
|
||||
|
||||
<ProgressBar x:Name="ProgressBar" Margin="0,0,0,27" Visibility="Hidden"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Height="27" Width="300" SnapsToDevicePixels="True"/>
|
||||
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,115,10" IsCancel="True" IsDefault="True"
|
||||
FontSize="14" HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||||
Width="100" Height="27"/>
|
||||
<Button x:Name="InstallButton" Content="Installieren" Margin="10,10,10,10"
|
||||
FontSize="14" HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||||
Width="100" Height="27"
|
||||
Click="InstallButton_Click"/>
|
||||
</Grid>
|
||||
</Window>
|
47
Elwig/Dialogs/UpdateDialog.xaml.cs
Normal file
47
Elwig/Dialogs/UpdateDialog.xaml.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using Elwig.Helpers;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace Elwig.Dialogs {
|
||||
public partial class UpdateDialog : Window {
|
||||
|
||||
public string Version { get; private set; }
|
||||
public string Url { get; private set; }
|
||||
|
||||
public UpdateDialog(string version, string url, long size) {
|
||||
Version = version;
|
||||
Url = url;
|
||||
InitializeComponent();
|
||||
VersionText.Text = version;
|
||||
SizeText.Text = $"{size / 1024 / 1024}";
|
||||
}
|
||||
|
||||
private async void InstallButton_Click(object sender, RoutedEventArgs evt) {
|
||||
Description.Visibility = Visibility.Hidden;
|
||||
ProgressBar.Visibility = Visibility.Visible;
|
||||
InstallButton.IsEnabled = false;
|
||||
await Install();
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
public async Task Install() {
|
||||
var fileName = Path.Combine(App.TempPath, $"Elwig-{Version}.exe");
|
||||
{
|
||||
using var stream = new FileStream(fileName, FileMode.Create);
|
||||
using var client = new HttpClient() {
|
||||
Timeout = TimeSpan.FromSeconds(5),
|
||||
};
|
||||
await client.DownloadAsync(Url, stream, new Progress<double>(p => {
|
||||
ProgressBar.Value = p * 100.0;
|
||||
}));
|
||||
}
|
||||
|
||||
Process.Start(fileName);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||
<Version>0.6.8</Version>
|
||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -31,14 +31,19 @@ namespace Elwig.Helpers {
|
||||
|
||||
public class Config {
|
||||
|
||||
private static readonly string[] TrueValues = ["1", "true", "yes", "on"];
|
||||
|
||||
private readonly string FileName;
|
||||
|
||||
public bool Debug;
|
||||
public string DatabaseFile = App.DataPath + "database.sqlite3";
|
||||
public string? DatabaseLog = null;
|
||||
public string? Branch = null;
|
||||
public string? UpdateUrl = null;
|
||||
public bool UpdateAuto = false;
|
||||
|
||||
public IList<ScaleConfig> Scales;
|
||||
private readonly List<ScaleConfig> ScaleList = [];
|
||||
private static readonly string[] trueValues = ["1", "true", "yes", "on"];
|
||||
|
||||
public Config(string filename) {
|
||||
FileName = filename;
|
||||
@ -53,7 +58,9 @@ namespace Elwig.Helpers {
|
||||
var log = config["database:log"];
|
||||
DatabaseLog = log != null ? Path.Combine(App.DataPath, log) : null;
|
||||
Branch = config["general:branch"];
|
||||
Debug = trueValues.Contains(config["general:debug"]?.ToLower());
|
||||
Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
|
||||
UpdateUrl = config["update:url"];
|
||||
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
|
||||
|
||||
var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key);
|
||||
ScaleList.Clear();
|
||||
@ -73,6 +80,10 @@ namespace Elwig.Helpers {
|
||||
if (Debug) file.Write("debug = true\r\n");
|
||||
file.Write($"\r\n[database]\r\nfile = {DatabaseFile}\r\n");
|
||||
if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n");
|
||||
file.Write($"\r\n[update]\r\n");
|
||||
if (UpdateUrl != null) file.Write($"url = {UpdateUrl}\r\n");
|
||||
if (UpdateAuto) file.Write($"auto = true\r\n");
|
||||
|
||||
foreach (var s in ScaleList) {
|
||||
file.Write($"\r\n[scale.{s.Id}]\r\ntype = {s.Type}\r\nmodel = {s.Model}\r\nconnection = {s.Connection}\r\n");
|
||||
if (s.Empty != null) file.Write($"empty = {s.Empty}\r\n");
|
||||
|
24
Elwig/Helpers/HttpClientExtensions.cs
Normal file
24
Elwig/Helpers/HttpClientExtensions.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public static class HttpClientExtensions {
|
||||
public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<double>? progress = null, CancellationToken cancellationToken = default) {
|
||||
using var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
|
||||
using var download = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
if (progress == null || !contentLength.HasValue) {
|
||||
await download.CopyToAsync(destination, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
var relativeProgress = new Progress<long>(totalBytes => progress.Report((double)totalBytes / contentLength.Value));
|
||||
await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken);
|
||||
progress.Report(100.0);
|
||||
}
|
||||
}
|
||||
}
|
25
Elwig/Helpers/StreamExtensions.cs
Normal file
25
Elwig/Helpers/StreamExtensions.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public static class StreamExtensions {
|
||||
public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long>? progress = null, CancellationToken cancellationToken = default) {
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
if (!source.CanRead) throw new ArgumentException("Has to be readable", nameof(source));
|
||||
ArgumentNullException.ThrowIfNull(destination);
|
||||
if (!destination.CanWrite) throw new ArgumentException("Has to be writable", nameof(destination));
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(bufferSize);
|
||||
|
||||
var buffer = new byte[bufferSize];
|
||||
long totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) {
|
||||
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
|
||||
totalBytesRead += bytesRead;
|
||||
progress?.Report(totalBytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,10 @@ using System.Text;
|
||||
using System.Numerics;
|
||||
using Elwig.Models.Entities;
|
||||
using Elwig.Helpers.Billing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.IO;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public static partial class Utils {
|
||||
@ -61,6 +65,8 @@ namespace Elwig.Helpers {
|
||||
return PhoneNrTypes.Where(t => t.Key == type).Select(t => t.Value).FirstOrDefault(type);
|
||||
}
|
||||
|
||||
private static readonly string[] TempWildcards = ["*.html", "*.pdf", "*.exe"];
|
||||
|
||||
private static readonly ushort[] Crc16ModbusTable = [
|
||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
|
||||
@ -302,7 +308,7 @@ namespace Elwig.Helpers {
|
||||
public static string GetSign<T>(T number) where T : INumber<T>
|
||||
=> T.Sign(number) switch {
|
||||
< 0 => "\u2212", // minus
|
||||
0 => "\u00b1", // plus minus
|
||||
0 => "\u00b1", // plus minus
|
||||
> 0 => "+",
|
||||
};
|
||||
|
||||
@ -378,5 +384,33 @@ namespace Elwig.Helpers {
|
||||
.Select(s => new Varibute(s, varieties, attributes, cultivations))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
[LibraryImport("wininet.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool InternetGetConnectedState(out int description, int reservedValue);
|
||||
|
||||
public static bool HasInternetConnectivity() {
|
||||
return InternetGetConnectedState(out var _, 0);
|
||||
}
|
||||
|
||||
public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string url) {
|
||||
try {
|
||||
using var client = new HttpClient() {
|
||||
Timeout = TimeSpan.FromSeconds(5),
|
||||
};
|
||||
var res = JsonNode.Parse(await client.GetStringAsync(url));
|
||||
var data = res!["data"]![0]!;
|
||||
return ((string)data["version"]!, (string)data["url"]!, (int)data["size"]!);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CleanupTempFiles() {
|
||||
var dir = new DirectoryInfo(App.TempPath);
|
||||
foreach (var file in TempWildcards.SelectMany(dir.EnumerateFiles)) {
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="HelpMenu" Header="Hilfe">
|
||||
<MenuItem Header="Über"/>
|
||||
<MenuItem x:Name="CheckForUpdatesButton" Header="Nach Updates suchen" Click="Menu_Help_Update_Click"/>
|
||||
<MenuItem x:Name="TestWindowButton" Header="Test-Fenster" Click="Menu_Help_TestWindow_Click"/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
@ -14,12 +14,13 @@ namespace Elwig.Windows {
|
||||
HelpMenu.Items.Remove(TestWindowButton);
|
||||
//QueryWindowButton.Visibility = Visibility.Hidden;
|
||||
}
|
||||
if (App.Config.UpdateUrl == null) CheckForUpdatesButton.IsEnabled = false;
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs evt) { }
|
||||
|
||||
private void Window_Closing(object sender, CancelEventArgs evt) {
|
||||
if (App.NumWindows > 1) {
|
||||
if (App.NumWindows > 1 && !App.ForceShutdown) {
|
||||
var res = MessageBox.Show("Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?",
|
||||
"Elwig beenden", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
if (res != MessageBoxResult.Yes) {
|
||||
@ -35,6 +36,10 @@ namespace Elwig.Windows {
|
||||
w.Show();
|
||||
}
|
||||
|
||||
private async void Menu_Help_Update_Click(object sender, RoutedEventArgs evt) {
|
||||
await App.CheckForUpdates();
|
||||
}
|
||||
|
||||
private void Menu_Database_Query_Click(object sender, RoutedEventArgs evt) {
|
||||
var w = new QueryWindow();
|
||||
w.Show();
|
||||
|
@ -10,6 +10,10 @@ file = database.sqlite3
|
||||
; Enables database logging
|
||||
;log = db.log
|
||||
|
||||
[update]
|
||||
url = https://www.necronda.net/elwig/files/elwig/latest?format=json
|
||||
auto = true
|
||||
|
||||
;[scale.1]
|
||||
;type = SysTec-IT
|
||||
;model = IT3000
|
||||
|
Reference in New Issue
Block a user