Auto update WIP

This commit is contained in:
Ben Olden-Cooligan 2018-09-16 13:53:45 -04:00
parent 5fbdcef477
commit 971d4b7e49
14 changed files with 473 additions and 18 deletions

View File

@ -16,7 +16,9 @@ namespace NAPS2.Automation
op.Wait();
}
public void ShowModalProgress(IOperation op) => throw new InvalidOperationException();
public void ShowModalProgress(IOperation op)
{
}
public void ShowBackgroundProgress(IOperation op) {
}

View File

@ -24,6 +24,8 @@ namespace NAPS2.Config
public HashSet<string> BackgroundOperations { get; set; } = new HashSet<string>();
public bool CheckForUpdates { get; set; }
public DateTime? LastUpdateCheckDate { get; set; }
public DateTime? FirstRunDate { get; set; }

View File

@ -213,6 +213,15 @@ namespace NAPS2.Lang.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Checking....
/// </summary>
internal static string CheckingForUpdates {
get {
return ResourceManager.GetString("CheckingForUpdates", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Choose Profile.
/// </summary>
@ -726,6 +735,15 @@ namespace NAPS2.Lang.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Install {0}.
/// </summary>
internal static string Install {
get {
return ResourceManager.GetString("Install", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Installation Complete.
/// </summary>
@ -843,6 +861,15 @@ namespace NAPS2.Lang.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to No updates available..
/// </summary>
internal static string NoUpdates {
get {
return ResourceManager.GetString("NoUpdates", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to OCR Progress.
/// </summary>
@ -1086,6 +1113,24 @@ namespace NAPS2.Lang.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Update Progress.
/// </summary>
internal static string UpdateProgress {
get {
return ResourceManager.GetString("UpdateProgress", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Updating....
/// </summary>
internal static string Updating {
get {
return ResourceManager.GetString("Updating", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Version {0}.
/// </summary>

View File

@ -462,4 +462,19 @@
<data name="RunningOcr" xml:space="preserve">
<value>Running OCR...</value>
</data>
<data name="UpdateProgress" xml:space="preserve">
<value>Update Progress</value>
</data>
<data name="Updating" xml:space="preserve">
<value>Updating...</value>
</data>
<data name="NoUpdates" xml:space="preserve">
<value>No updates available.</value>
</data>
<data name="CheckingForUpdates" xml:space="preserve">
<value>Checking...</value>
</data>
<data name="Install" xml:space="preserve">
<value>Install {0}</value>
</data>
</root>

View File

@ -167,6 +167,7 @@
<Compile Include="Ocr\Tesseract304XpEngine.cs" />
<Compile Include="Ocr\Tesseract304Engine.cs" />
<Compile Include="Ocr\Tesseract302Engine.cs" />
<Compile Include="Operation\OperationProgressType.cs" />
<Compile Include="Platform\ISystemCompat.cs" />
<Compile Include="Platform\LinuxSystemCompat.cs" />
<Compile Include="Platform\PlatformCompat.cs" />
@ -191,6 +192,9 @@
<Compile Include="Scan\Sane\SaneWrapper.cs" />
<Compile Include="Scan\Wia\WiaApi.mono.cs" />
<Compile Include="Scan\Wia\WiaState.mono.cs" />
<Compile Include="Update\UpdateOperation.cs" />
<Compile Include="Update\UpdateInfo.cs" />
<Compile Include="Update\UpdateChecker.cs" />
<Compile Include="Util\DeferredAction.cs" />
<Compile Include="Util\ExceptionExtensions.cs" />
<Compile Include="Platform\IRuntimeCompat.cs" />

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace NAPS2.Operation
{
public enum OperationProgressType
{
Default,
MB
}
}

View File

@ -16,5 +16,7 @@ namespace NAPS2.Operation
public int MaxProgress { get; set; }
public bool IndeterminateProgress { get; set; }
public OperationProgressType ProgressType { get; set; }
}
}

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using NAPS2.Operation;
using Newtonsoft.Json.Linq;
namespace NAPS2.Update
{
public class UpdateChecker
{
private const string UPDATE_CHECK_ENDPOINT = "https://www.naps2.com/api/v1/update";
#if STANDALONE
private const string UPDATE_FILE_EXT = "zip";
#elif INSTALLER_MSI
private const string UPDATE_FILE_EXT = "msi";
#else
private const string UPDATE_FILE_EXT = "exe";
#endif
private readonly IOperationFactory operationFactory;
private readonly IOperationProgress operationProgress;
public UpdateChecker(IOperationFactory operationFactory, IOperationProgress operationProgress)
{
this.operationFactory = operationFactory;
this.operationProgress = operationProgress;
}
public async Task<UpdateInfo> CheckForUpdates()
{
var json = await GetJson(UPDATE_CHECK_ENDPOINT);
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
foreach (var release in json.Value<JArray>("versions"))
{
var versionName = release.Value<string>("name");
var version = ParseVersion(versionName);
var gte = release["requires"].Value<string>("gte");
var gteVersion = gte != null ? ParseVersion(gte) : null;
if ((gteVersion == null || currentVersion >= gteVersion))// && currentVersion < version)
{
var updateFile = release["files"].Value<JToken>(UPDATE_FILE_EXT);
if (updateFile != null)
{
return new UpdateInfo
{
Name = versionName,
DownloadUrl = updateFile.Value<string>("url"),
Sha1 = Convert.FromBase64String(updateFile.Value<string>("sha1")),
Signature = Convert.FromBase64String(updateFile.Value<string>("sig"))
};
}
}
}
return null;
}
public UpdateOperation StartUpdate(UpdateInfo update)
{
var op = operationFactory.Create<UpdateOperation>();
op.Start(update);
operationProgress.ShowModalProgress(op);
return op;
}
private Version ParseVersion(string name)
{
return Version.Parse(name.Replace("b", "."));
}
private async Task<JObject> GetJson(string url)
{
return await Task.Factory.StartNew(() =>
{
using (var client = new WebClient())
{
var response = client.DownloadString(url);
return JObject.Parse(response);
}
});
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace NAPS2.Update
{
public class UpdateInfo
{
public string Name { get; set; }
public string DownloadUrl { get; set; }
public byte[] Sha1 { get; set; }
public byte[] Signature { get; set; }
}
}

View File

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using NAPS2.Lang.Resources;
using NAPS2.Operation;
namespace NAPS2.Update
{
public class UpdateOperation : OperationBase
{
private WebClient client;
private UpdateInfo update;
private string tempFolder;
private string tempPath;
static UpdateOperation()
{
try
{
const int tls12 = 3072;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)tls12;
}
catch (NotSupportedException)
{
}
}
public UpdateOperation()
{
ProgressTitle = MiscResources.UpdateProgress;
AllowBackground = true;
AllowCancel = true;
Status = new OperationStatus
{
StatusText = MiscResources.Updating,
ProgressType = OperationProgressType.MB
};
}
public void Start(UpdateInfo updateInfo)
{
update = updateInfo;
tempFolder = Path.Combine(Paths.Temp, Path.GetRandomFileName());
Directory.CreateDirectory(tempFolder);
tempPath = Path.Combine(tempFolder, updateInfo.DownloadUrl.Substring(updateInfo.DownloadUrl.LastIndexOf('/') + 1));
client = new WebClient();
client.DownloadProgressChanged += DownloadProgress;
client.DownloadFileCompleted += DownloadCompleted;
client.DownloadFileAsync(new Uri(updateInfo.DownloadUrl), tempPath);
}
private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
// TODO: Verify sha1/sig
// TODO: Standalone install
Process.Start(tempPath);
// TODO: Clean up temp file somehow
InvokeFinished();
Application.OpenForms.OfType<Form>().FirstOrDefault()?.Close();
}
private void DownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
Status.CurrentProgress = (int)e.BytesReceived;
Status.MaxProgress = (int)e.TotalBytesToReceive;
InvokeStatusChanged();
}
}
}

View File

@ -41,6 +41,9 @@ namespace NAPS2.WinForms
this.label1 = new System.Windows.Forms.Label();
this.linkLabel2 = new System.Windows.Forms.LinkLabel();
this.btnDonate = new System.Windows.Forms.PictureBox();
this.cbCheckForUpdates = new System.Windows.Forms.CheckBox();
this.lblUpdateStatus = new System.Windows.Forms.Label();
this.linkInstall = new System.Windows.Forms.LinkLabel();
((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.btnDonate)).BeginInit();
this.SuspendLayout();
@ -100,10 +103,31 @@ namespace NAPS2.WinForms
this.btnDonate.TabStop = false;
this.btnDonate.Click += new System.EventHandler(this.btnDonate_Click);
//
// cbCheckForUpdates
//
resources.ApplyResources(this.cbCheckForUpdates, "cbCheckForUpdates");
this.cbCheckForUpdates.Name = "cbCheckForUpdates";
this.cbCheckForUpdates.UseVisualStyleBackColor = true;
this.cbCheckForUpdates.CheckedChanged += new System.EventHandler(this.cbCheckForUpdates_CheckedChanged);
//
// lblUpdateStatus
//
resources.ApplyResources(this.lblUpdateStatus, "lblUpdateStatus");
this.lblUpdateStatus.Name = "lblUpdateStatus";
//
// linkInstall
//
resources.ApplyResources(this.linkInstall, "linkInstall");
this.linkInstall.Name = "linkInstall";
this.linkInstall.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkInstall_LinkClicked);
//
// FAbout
//
resources.ApplyResources(this, "$this");
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.linkInstall);
this.Controls.Add(this.lblUpdateStatus);
this.Controls.Add(this.cbCheckForUpdates);
this.Controls.Add(this.btnDonate);
this.Controls.Add(this.linkLabel2);
this.Controls.Add(this.label1);
@ -135,5 +159,8 @@ namespace NAPS2.WinForms
private System.Windows.Forms.Label label1;
private System.Windows.Forms.LinkLabel linkLabel2;
private System.Windows.Forms.PictureBox btnDonate;
private System.Windows.Forms.CheckBox cbCheckForUpdates;
private System.Windows.Forms.Label lblUpdateStatus;
private System.Windows.Forms.LinkLabel linkInstall;
}
}

View File

@ -7,13 +7,24 @@ using System.Reflection;
using System.Windows.Forms;
using NAPS2.Config;
using NAPS2.Lang.Resources;
using NAPS2.Update;
using NAPS2.Util;
namespace NAPS2.WinForms
{
partial class FAbout : FormBase
{
public FAbout(AppConfigManager appConfigManager)
private readonly IUserConfigManager userConfigManager;
private readonly UpdateChecker updateChecker;
private bool hasCheckedForUpdates;
private UpdateInfo update;
public FAbout(AppConfigManager appConfigManager, IUserConfigManager userConfigManager, UpdateChecker updateChecker)
{
this.userConfigManager = userConfigManager;
this.updateChecker = updateChecker;
RestoreFormState = false;
InitializeComponent();
labelProductName.Text = AssemblyProduct;
@ -31,6 +42,85 @@ namespace NAPS2.WinForms
}
}
protected override void OnLoad(object sender, EventArgs eventArgs)
{
new LayoutManager(this)
.Bind(logoPictureBox)
.TopTo(() => Height / 2)
.Activate();
cbCheckForUpdates.Checked = userConfigManager.Config.CheckForUpdates;
UpdateControls();
DoUpdateCheck();
}
private void DoUpdateCheck()
{
if (cbCheckForUpdates.Checked)
{
updateChecker.CheckForUpdates().ContinueWith(task =>
{
update = task.Result;
hasCheckedForUpdates = true;
SafeInvoke(UpdateControls);
});
}
}
private void UpdateControls()
{
const int margin = 5;
if (cbCheckForUpdates.Checked)
{
if (lblUpdateStatus.Visible == false && linkInstall.Visible == false)
{
ConditionalControls.Show(lblUpdateStatus, margin);
}
if (hasCheckedForUpdates)
{
if (update == null)
{
lblUpdateStatus.Text = MiscResources.NoUpdates;
lblUpdateStatus.Visible = true;
linkInstall.Visible = false;
}
else
{
linkInstall.Text = string.Format(MiscResources.Install, update.Name);
lblUpdateStatus.Visible = false;
linkInstall.Visible = true;
}
}
else
{
lblUpdateStatus.Text = MiscResources.CheckingForUpdates;
lblUpdateStatus.Visible = true;
linkInstall.Visible = false;
}
}
else
{
ConditionalControls.Hide(lblUpdateStatus, margin);
ConditionalControls.Hide(linkInstall, margin);
}
}
private void cbCheckForUpdates_CheckedChanged(object sender, EventArgs e)
{
userConfigManager.Config.CheckForUpdates = cbCheckForUpdates.Checked;
userConfigManager.Save();
UpdateControls();
DoUpdateCheck();
}
private void linkInstall_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (update != null)
{
updateChecker.StartUpdate(update);
}
}
#region Assembly Attribute Accessors
private static string GetAssemblyAttributeValue<T>(Func<T, string> selector)

View File

@ -148,7 +148,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;labelProductName.ZOrder" xml:space="preserve">
<value>6</value>
<value>9</value>
</data>
<data name="labelVersion.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
@ -178,13 +178,13 @@
<value>$this</value>
</data>
<data name="&gt;&gt;labelVersion.ZOrder" xml:space="preserve">
<value>7</value>
<value>10</value>
</data>
<data name="labelCopyright.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
</data>
<data name="labelCopyright.Location" type="System.Drawing.Point, System.Drawing">
<value>139, 69</value>
<value>139, 116</value>
</data>
<data name="labelCopyright.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
<value>6, 12, 3, 0</value>
@ -208,7 +208,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;labelCopyright.ZOrder" xml:space="preserve">
<value>8</value>
<value>11</value>
</data>
<data name="okButton.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
<value>Bottom, Right</value>
@ -220,7 +220,7 @@
<value>NoControl</value>
</data>
<data name="okButton.Location" type="System.Drawing.Point, System.Drawing">
<value>336, 100</value>
<value>336, 145</value>
</data>
<data name="okButton.Size" type="System.Drawing.Size, System.Drawing">
<value>72, 24</value>
@ -241,7 +241,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;okButton.ZOrder" xml:space="preserve">
<value>3</value>
<value>6</value>
</data>
<data name="logoPictureBox.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
@ -414,7 +414,7 @@
<value>NoControl</value>
</data>
<data name="logoPictureBox.Location" type="System.Drawing.Point, System.Drawing">
<value>10, 8</value>
<value>10, 30</value>
</data>
<data name="logoPictureBox.Size" type="System.Drawing.Size, System.Drawing">
<value>120, 120</value>
@ -435,7 +435,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;logoPictureBox.ZOrder" xml:space="preserve">
<value>5</value>
<value>8</value>
</data>
<data name="linkLabel1.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
@ -462,13 +462,13 @@
<value>$this</value>
</data>
<data name="&gt;&gt;linkLabel1.ZOrder" xml:space="preserve">
<value>4</value>
<value>7</value>
</data>
<data name="label1.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
</data>
<data name="label1.Location" type="System.Drawing.Point, System.Drawing">
<value>139, 96</value>
<value>139, 143</value>
</data>
<data name="label1.Size" type="System.Drawing.Size, System.Drawing">
<value>59, 13</value>
@ -489,13 +489,13 @@
<value>$this</value>
</data>
<data name="&gt;&gt;label1.ZOrder" xml:space="preserve">
<value>2</value>
<value>5</value>
</data>
<data name="linkLabel2.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
</data>
<data name="linkLabel2.Location" type="System.Drawing.Point, System.Drawing">
<value>140, 109</value>
<value>140, 156</value>
</data>
<data name="linkLabel2.Size" type="System.Drawing.Size, System.Drawing">
<value>171, 13</value>
@ -516,7 +516,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;linkLabel2.ZOrder" xml:space="preserve">
<value>1</value>
<value>4</value>
</data>
<data name="btnDonate.ImeMode" type="System.Windows.Forms.ImeMode, System.Windows.Forms">
<value>NoControl</value>
@ -543,6 +543,84 @@
<value>$this</value>
</data>
<data name="&gt;&gt;btnDonate.ZOrder" xml:space="preserve">
<value>3</value>
</data>
<data name="cbCheckForUpdates.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
</data>
<data name="cbCheckForUpdates.Location" type="System.Drawing.Point, System.Drawing">
<value>141, 70</value>
</data>
<data name="cbCheckForUpdates.Size" type="System.Drawing.Size, System.Drawing">
<value>113, 17</value>
</data>
<data name="cbCheckForUpdates.TabIndex" type="System.Int32, mscorlib">
<value>36</value>
</data>
<data name="cbCheckForUpdates.Text" xml:space="preserve">
<value>Check for updates</value>
</data>
<data name="&gt;&gt;cbCheckForUpdates.Name" xml:space="preserve">
<value>cbCheckForUpdates</value>
</data>
<data name="&gt;&gt;cbCheckForUpdates.Type" xml:space="preserve">
<value>System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="&gt;&gt;cbCheckForUpdates.Parent" xml:space="preserve">
<value>$this</value>
</data>
<data name="&gt;&gt;cbCheckForUpdates.ZOrder" xml:space="preserve">
<value>2</value>
</data>
<data name="lblUpdateStatus.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
</data>
<data name="lblUpdateStatus.Location" type="System.Drawing.Point, System.Drawing">
<value>139, 90</value>
</data>
<data name="lblUpdateStatus.Size" type="System.Drawing.Size, System.Drawing">
<value>0, 13</value>
</data>
<data name="lblUpdateStatus.TabIndex" type="System.Int32, mscorlib">
<value>37</value>
</data>
<data name="&gt;&gt;lblUpdateStatus.Name" xml:space="preserve">
<value>lblUpdateStatus</value>
</data>
<data name="&gt;&gt;lblUpdateStatus.Type" xml:space="preserve">
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="&gt;&gt;lblUpdateStatus.Parent" xml:space="preserve">
<value>$this</value>
</data>
<data name="&gt;&gt;lblUpdateStatus.ZOrder" xml:space="preserve">
<value>1</value>
</data>
<data name="linkInstall.AutoSize" type="System.Boolean, mscorlib">
<value>True</value>
</data>
<data name="linkInstall.Location" type="System.Drawing.Point, System.Drawing">
<value>139, 90</value>
</data>
<data name="linkInstall.Size" type="System.Drawing.Size, System.Drawing">
<value>0, 13</value>
</data>
<data name="linkInstall.TabIndex" type="System.Int32, mscorlib">
<value>38</value>
</data>
<data name="linkInstall.Visible" type="System.Boolean, mscorlib">
<value>False</value>
</data>
<data name="&gt;&gt;linkInstall.Name" xml:space="preserve">
<value>linkInstall</value>
</data>
<data name="&gt;&gt;linkInstall.Type" xml:space="preserve">
<value>System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="&gt;&gt;linkInstall.Parent" xml:space="preserve">
<value>$this</value>
</data>
<data name="&gt;&gt;linkInstall.ZOrder" xml:space="preserve">
<value>0</value>
</data>
<metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@ -552,7 +630,7 @@
<value>6, 13</value>
</data>
<data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing">
<value>420, 136</value>
<value>420, 181</value>
</data>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
@ -591,6 +669,6 @@
<value>FAbout</value>
</data>
<data name="&gt;&gt;$this.Type" xml:space="preserve">
<value>NAPS2.WinForms.FormBase, NAPS2.Core, Version=5.8.2.103, Culture=neutral, PublicKeyToken=null</value>
<value>NAPS2.WinForms.FormBase, NAPS2.Core, Version=5.8.2.24548, Culture=neutral, PublicKeyToken=null</value>
</data>
</root>

View File

@ -100,7 +100,9 @@ namespace NAPS2.WinForms
}
else
{
labelNumber.Text = string.Format(MiscResources.ProgressFormat, status.CurrentProgress, status.MaxProgress);
labelNumber.Text = status.ProgressType == OperationProgressType.MB
? string.Format(MiscResources.SizeProgress, (status.CurrentProgress / 1000000.0).ToString("f1"), (status.MaxProgress / 1000000.0).ToString("f1"))
: string.Format(MiscResources.ProgressFormat, status.CurrentProgress, status.MaxProgress);
progressBar.Style = ProgressBarStyle.Continuous;
progressBar.Maximum = status.MaxProgress;
progressBar.Value = status.CurrentProgress;