2121using Microsoft . Win32 ;
2222using Microsoft . Windows . AppLifecycle ;
2323using Microsoft . Windows . AppNotifications ;
24+ using NetSparkleUpdater . Interfaces ;
2425using Serilog ;
2526using LaunchActivatedEventArgs = Microsoft . UI . Xaml . LaunchActivatedEventArgs ;
2627
2728namespace Coder . Desktop . App ;
2829
2930public partial class App : Application
3031{
31- private readonly IServiceProvider _services ;
32-
33- private bool _handleWindowClosed = true ;
3432 private const string MutagenControllerConfigSection = "MutagenController" ;
33+ private const string UpdaterConfigSection = "Updater" ;
3534
3635#if ! DEBUG
3736 private const string ConfigSubKey = @"SOFTWARE\Coder Desktop\App" ;
38- private const string logFilename = "app.log" ;
37+ private const string LogFilename = "app.log" ;
38+ private const string DefaultLogLevel = "Information" ;
3939#else
4040 private const string ConfigSubKey = @"SOFTWARE\Coder Desktop\DebugApp" ;
41- private const string logFilename = "debug-app.log" ;
41+ private const string LogFilename = "debug-app.log" ;
42+ private const string DefaultLogLevel = "Debug" ;
4243#endif
4344
45+ // HACK: This is exposed for dispatcher queue access. The notifier uses
46+ // this to ensure action callbacks run in the UI thread (as
47+ // activation events aren't in the main thread).
48+ public TrayWindow ? TrayWindow ;
49+
50+ private readonly IServiceProvider _services ;
4451 private readonly ILogger < App > _logger ;
4552 private readonly IUriHandler _uriHandler ;
53+ private readonly IUserNotifier _userNotifier ;
54+
55+ private bool _handleWindowClosed = true ;
4656
4757 public App ( )
4858 {
@@ -55,7 +65,17 @@ public App()
5565 configBuilder . Add (
5666 new RegistryConfigurationSource ( Registry . LocalMachine , ConfigSubKey ) ) ;
5767 configBuilder . Add (
58- new RegistryConfigurationSource ( Registry . CurrentUser , ConfigSubKey ) ) ;
68+ new RegistryConfigurationSource (
69+ Registry . CurrentUser ,
70+ ConfigSubKey ,
71+ // Block "Updater:" configuration from HKCU, so that updater
72+ // settings can only be set at the HKLM level.
73+ //
74+ // HACK: This isn't super robust, but the security risk is
75+ // minor anyway. Malicious apps running as the user could
76+ // likely override this setting by altering the memory of
77+ // this app.
78+ UpdaterConfigSection + ":" ) ) ;
5979
6080 var services = builder . Services ;
6181
@@ -81,6 +101,12 @@ public App()
81101 services . AddSingleton < IRdpConnector , RdpConnector > ( ) ;
82102 services . AddSingleton < IUriHandler , UriHandler > ( ) ;
83103
104+ services . AddOptions < UpdaterConfig > ( )
105+ . Bind ( builder . Configuration . GetSection ( UpdaterConfigSection ) ) ;
106+ services . AddSingleton < IUpdaterUpdateAvailableViewModelFactory , UpdaterUpdateAvailableViewModelFactory > ( ) ;
107+ services . AddSingleton < IUIFactory , CoderSparkleUIFactory > ( ) ;
108+ services . AddSingleton < IUpdateController , SparkleUpdateController > ( ) ;
109+
84110 // SignInWindow views and view models
85111 services . AddTransient < SignInViewModel > ( ) ;
86112 services . AddTransient < SignInWindow > ( ) ;
@@ -107,8 +133,9 @@ public App()
107133 services . AddTransient < TrayWindow > ( ) ;
108134
109135 _services = services . BuildServiceProvider ( ) ;
110- _logger = ( ILogger < App > ) _services . GetService ( typeof ( ILogger < App > ) ) ! ;
111- _uriHandler = ( IUriHandler ) _services . GetService ( typeof ( IUriHandler ) ) ! ;
136+ _logger = _services . GetRequiredService < ILogger < App > > ( ) ;
137+ _uriHandler = _services . GetRequiredService < IUriHandler > ( ) ;
138+ _userNotifier = _services . GetRequiredService < IUserNotifier > ( ) ;
112139
113140 InitializeComponent ( ) ;
114141 }
@@ -129,6 +156,18 @@ public async Task ExitApplication()
129156 protected override void OnLaunched ( LaunchActivatedEventArgs args )
130157 {
131158 _logger . LogInformation ( "new instance launched" ) ;
159+
160+ // Prevent the TrayWindow from closing, just hide it.
161+ if ( TrayWindow != null )
162+ throw new InvalidOperationException ( "OnLaunched was called multiple times? TrayWindow is already set" ) ;
163+ TrayWindow = _services . GetRequiredService < TrayWindow > ( ) ;
164+ TrayWindow . Closed += ( _ , closedArgs ) =>
165+ {
166+ if ( ! _handleWindowClosed ) return ;
167+ closedArgs . Handled = true ;
168+ TrayWindow . AppWindow . Hide ( ) ;
169+ } ;
170+
132171 // Start connecting to the manager in the background.
133172 var rpcController = _services . GetRequiredService < IRpcController > ( ) ;
134173 if ( rpcController . GetState ( ) . RpcLifecycle == RpcLifecycle . Disconnected )
@@ -179,15 +218,6 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
179218
180219 syncSessionCts . Dispose ( ) ;
181220 } , CancellationToken . None ) ;
182-
183- // Prevent the TrayWindow from closing, just hide it.
184- var trayWindow = _services . GetRequiredService < TrayWindow > ( ) ;
185- trayWindow . Closed += ( _ , closedArgs ) =>
186- {
187- if ( ! _handleWindowClosed ) return ;
188- closedArgs . Handled = true ;
189- trayWindow . AppWindow . Hide ( ) ;
190- } ;
191221 }
192222
193223 public void OnActivated ( object ? sender , AppActivationArguments args )
@@ -229,27 +259,36 @@ public void OnActivated(object? sender, AppActivationArguments args)
229259
230260 public void HandleNotification ( AppNotificationManager ? sender , AppNotificationActivatedEventArgs args )
231261 {
232- // right now, we don't do anything other than log
233- _logger . LogInformation ( "handled notification activation" ) ;
262+ _logger . LogInformation ( "handled notification activation: {Argument}" , args . Argument ) ;
263+ _userNotifier . HandleActivation ( args ) ;
234264 }
235265
236266 private static void AddDefaultConfig ( IConfigurationBuilder builder )
237267 {
238268 var logPath = Path . Combine (
239269 Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ,
240270 "CoderDesktop" ,
241- logFilename ) ;
271+ LogFilename ) ;
242272 builder . AddInMemoryCollection ( new Dictionary < string , string ? >
243273 {
244274 [ MutagenControllerConfigSection + ":MutagenExecutablePath" ] = @"C:\mutagen.exe" ,
275+
245276 [ "Serilog:Using:0" ] = "Serilog.Sinks.File" ,
246- [ "Serilog:MinimumLevel" ] = "Information" ,
277+ [ "Serilog:MinimumLevel" ] = DefaultLogLevel ,
247278 [ "Serilog:Enrich:0" ] = "FromLogContext" ,
248279 [ "Serilog:WriteTo:0:Name" ] = "File" ,
249280 [ "Serilog:WriteTo:0:Args:path" ] = logPath ,
250281 [ "Serilog:WriteTo:0:Args:outputTemplate" ] =
251282 "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}" ,
252283 [ "Serilog:WriteTo:0:Args:rollingInterval" ] = "Day" ,
284+
285+ #if DEBUG
286+ [ "Serilog:Using:1" ] = "Serilog.Sinks.Debug" ,
287+ [ "Serilog:Enrich:1" ] = "FromLogContext" ,
288+ [ "Serilog:WriteTo:1:Name" ] = "Debug" ,
289+ [ "Serilog:WriteTo:1:Args:outputTemplate" ] =
290+ "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}" ,
291+ #endif
253292 } ) ;
254293 }
255294}
0 commit comments