WIP - Install/update support

This commit is contained in:
2021-12-20 12:28:50 -05:00
parent 4f80379692
commit 37ba989310
9 changed files with 266 additions and 63 deletions

View File

@@ -1,10 +1,11 @@
using System; using Common.Helpers;
using System.Windows;
using Common.Helpers;
using Common.IO; using Common.IO;
using Common.Wpf.Extensions; using Common.Wpf.Extensions;
using Squirrel;
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Settings = WorkIndicator.Properties.Settings; using Settings = WorkIndicator.Properties.Settings;
namespace WorkIndicator namespace WorkIndicator
@@ -13,29 +14,69 @@ namespace WorkIndicator
{ {
private IDisposable _isolationHandle; private IDisposable _isolationHandle;
public static string UpdateUrl = "https://github.com/ckaczor/WorkIndicator";
private Dispatcher _dispatcher;
[STAThread]
public static void Main(string[] args)
{
SquirrelAwareApp.HandleEvents(onAppUpdate: version => Common.Settings.Extensions.RestoreSettings());
var application = new App();
application.InitializeComponent();
application.Run();
}
protected override void OnStartup(StartupEventArgs e) protected override void OnStartup(StartupEventArgs e)
{ {
base.OnStartup(e); base.OnStartup(e);
// Create an isolation handle to see if we are already running _dispatcher = Dispatcher.CurrentDispatcher;
_isolationHandle = ApplicationIsolation.GetIsolationHandle();
// If there is another copy then pass it the command line and exit
if (_isolationHandle == null)
{
InterprocessMessageSender.SendMessage(Environment.CommandLine);
Shutdown();
return;
}
// Set automatic start into the registry
Current.SetStartWithWindows(Settings.Default.StartWithWindows);
// Initialize the tray icon // Initialize the tray icon
TrayIcon.Initialize(); TrayIcon.Initialize();
// Initialize the light controller Task.Factory.StartNew(UpdateApp).ContinueWith(task => StartUpdate(task.Result.Result));
LightController.Initialize(); }
private void StartUpdate(bool updateRequired)
{
if (updateRequired)
return;
Task.Factory.StartNew(() =>
{
// Create an isolation handle to see if we are already running
_isolationHandle = ApplicationIsolation.GetIsolationHandle();
// If there is another copy then pass it the command line and exit
if (_isolationHandle == null)
{
InterprocessMessageSender.SendMessage(Environment.CommandLine);
Shutdown();
return;
}
// Set automatic start into the registry
Current.SetStartWithWindows(Settings.Default.StartWithWindows);
// Initialize the light controller
LightController.Initialize();
});
}
private async Task<bool> UpdateApp()
{
return await UpdateCheck.CheckUpdate(HandleUpdateStatus);
}
private void HandleUpdateStatus(UpdateCheck.UpdateStatus status, string message)
{
if (status == UpdateCheck.UpdateStatus.None)
message = WorkIndicator.Properties.Resources.Loading;
_dispatcher.Invoke(() => TrayIcon.SetText(message));
} }
protected override void OnExit(ExitEventArgs e) protected override void OnExit(ExitEventArgs e)

2
Common

Submodule Common updated: 81ef8f451c...6f528f7b59

View File

@@ -1,55 +1,21 @@
using System.Reflection; using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows; using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WorkIndicator")] [assembly: AssemblyTitle("WorkIndicator")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("Chris Kaczor")]
[assembly: AssemblyProduct("WorkIndicator")] [assembly: AssemblyProduct("WorkIndicator")]
[assembly: AssemblyCopyright("Copyright © 2012")] [assembly: AssemblyCopyright("Copyright © Chris Kaczor 2012")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
//In order to begin building localizable applications, set [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyMetadata("SquirrelAwareVersion", "1")]

View File

@@ -19,7 +19,7 @@ namespace WorkIndicator.Properties {
// class via a tool like ResGen or Visual Studio. // class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen // To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project. // with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources { public class Resources {
@@ -124,6 +124,15 @@ namespace WorkIndicator.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Checking for update....
/// </summary>
public static string CheckingForUpdate {
get {
return ResourceManager.GetString("CheckingForUpdate", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Enabled. /// Looks up a localized string similar to Enabled.
/// </summary> /// </summary>
@@ -169,6 +178,15 @@ namespace WorkIndicator.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Downloading update....
/// </summary>
public static string DownloadingUpdate {
get {
return ResourceManager.GetString("DownloadingUpdate", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Edit. /// Looks up a localized string similar to Edit.
/// </summary> /// </summary>
@@ -196,6 +214,33 @@ namespace WorkIndicator.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Installing update....
/// </summary>
public static string InstallingUpdate {
get {
return ResourceManager.GetString("InstallingUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Loading....
/// </summary>
public static string Loading {
get {
return ResourceManager.GetString("Loading", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No update found.
/// </summary>
public static string NoUpdate {
get {
return ResourceManager.GetString("NoUpdate", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to OK. /// Looks up a localized string similar to OK.
/// </summary> /// </summary>
@@ -278,6 +323,15 @@ namespace WorkIndicator.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Restarting application....
/// </summary>
public static string RestartingAfterUpdate {
get {
return ResourceManager.GetString("RestartingAfterUpdate", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to _Start when Windows starts. /// Looks up a localized string similar to _Start when Windows starts.
/// </summary> /// </summary>
@@ -314,6 +368,15 @@ namespace WorkIndicator.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Updating application....
/// </summary>
public static string Updating {
get {
return ResourceManager.GetString("Updating", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to _Enabled:. /// Looks up a localized string similar to _Enabled:.
/// </summary> /// </summary>

View File

@@ -220,4 +220,25 @@
<data name="Working" xml:space="preserve"> <data name="Working" xml:space="preserve">
<value>Working</value> <value>Working</value>
</data> </data>
<data name="CheckingForUpdate" xml:space="preserve">
<value>Checking for update...</value>
</data>
<data name="DownloadingUpdate" xml:space="preserve">
<value>Downloading update...</value>
</data>
<data name="NoUpdate" xml:space="preserve">
<value>No update found</value>
</data>
<data name="Updating" xml:space="preserve">
<value>Updating application...</value>
</data>
<data name="RestartingAfterUpdate" xml:space="preserve">
<value>Restarting application...</value>
</data>
<data name="InstallingUpdate" xml:space="preserve">
<value>Installing update...</value>
</data>
<data name="Loading" xml:space="preserve">
<value>Loading...</value>
</data>
</root> </root>

View File

@@ -68,6 +68,11 @@ namespace WorkIndicator
_initialized = true; _initialized = true;
} }
public static void SetText(string text)
{
_trayIcon.Text = text;
}
static void HandleContextMenuStripOpening(object sender, System.ComponentModel.CancelEventArgs e) static void HandleContextMenuStripOpening(object sender, System.ComponentModel.CancelEventArgs e)
{ {
foreach (ToolStripItem menuItem in _trayIcon.ContextMenuStrip.Items) foreach (ToolStripItem menuItem in _trayIcon.ContextMenuStrip.Items)

71
UpdateCheck.cs Normal file
View File

@@ -0,0 +1,71 @@
using Squirrel;
using System;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
namespace WorkIndicator
{
public static class UpdateCheck
{
public enum UpdateStatus
{
Checking,
None,
Downloading,
Installing,
Restarting
}
public delegate void UpdateStatusDelegate(UpdateStatus updateStatus, string message);
public static Version LocalVersion => Assembly.GetEntryAssembly().GetName().Version;
public static async Task<bool> CheckUpdate(UpdateStatusDelegate onUpdateStatus)
{
try
{
onUpdateStatus.Invoke(UpdateStatus.Checking, Properties.Resources.CheckingForUpdate);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
using (var updateManager = await UpdateManager.GitHubUpdateManager(App.UpdateUrl))
{
var updates = await updateManager.CheckForUpdate();
var lastVersion = updates?.ReleasesToApply?.OrderBy(releaseEntry => releaseEntry.Version).LastOrDefault();
if (lastVersion == null)
{
onUpdateStatus.Invoke(UpdateStatus.None, Properties.Resources.NoUpdate);
return false;
}
onUpdateStatus.Invoke(UpdateStatus.Downloading, Properties.Resources.DownloadingUpdate);
Common.Settings.Extensions.BackupSettings();
await updateManager.DownloadReleases(new[] { lastVersion });
onUpdateStatus.Invoke(UpdateStatus.Installing, Properties.Resources.InstallingUpdate);
await updateManager.ApplyReleases(updates);
await updateManager.UpdateApp();
}
onUpdateStatus.Invoke(UpdateStatus.Restarting, Properties.Resources.RestartingAfterUpdate);
UpdateManager.RestartApp();
return true;
}
catch (Exception exception)
{
Console.WriteLine(exception);
return false;
}
}
}
}

View File

@@ -143,10 +143,11 @@
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ApplicationDefinition Include="App.xaml"> <Compile Include="UpdateCheck.cs" />
<Page Include="App.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </Page>
<Compile Include="AudioWatcher.cs" /> <Compile Include="AudioWatcher.cs" />
<Compile Include="LightController.cs" /> <Compile Include="LightController.cs" />
<Compile Include="Delcom\StoplightIndicator.cs" /> <Compile Include="Delcom\StoplightIndicator.cs" />
@@ -188,6 +189,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
<None Include="app.config" /> <None Include="app.config" />
<None Include="appveyor.yml" />
<None Include="LICENSE.md" /> <None Include="LICENSE.md" />
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
@@ -234,6 +236,9 @@
<PackageReference Include="Newtonsoft.Json"> <PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version> <Version>13.0.1</Version>
</PackageReference> </PackageReference>
<PackageReference Include="squirrel.windows">
<Version>2.0.1</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">

31
appveyor.yml Normal file
View File

@@ -0,0 +1,31 @@
version: 1.0.{build}
pull_requests:
do_not_increment_build_number: true
skip_tags: true
image: Visual Studio 2017
configuration: Release
assembly_info:
patch: true
file: 'Properties\AssemblyInfo.cs'
assembly_version: '{version}'
assembly_file_version: '{version}'
build:
project: WorkIndicator.sln
verbosity: minimal
after_build:
- ps: >-
nuget pack WorkIndicator.nuspec -Version $env:APPVEYOR_BUILD_VERSION -Properties Configuration=Release -OutputDirectory bin\Release\
$squirrel = ".\packages\squirrel.windows.*\tools\Squirrel.exe"
.$squirrel -releasify ".\bin\$env:CONFIGURATION\WorkIndicator.$env:APPVEYOR_BUILD_VERSION.nupkg" | Write-Output
artifacts:
- path: Releases\*
name: Releases
deploy:
- provider: Environment
name: GitHub
install:
- cmd: git submodule update --init --recursive
before_build:
- cmd: nuget restore