Skip to content

Commit 2659136

Browse files
authored
Feature: Added an option to keep Files running in the background for quicker startup time (#13236)
1 parent 3efdaef commit 2659136

File tree

13 files changed

+142
-47
lines changed

13 files changed

+142
-47
lines changed

src/Files.App/App.xaml.cs

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using CommunityToolkit.WinUI.Helpers;
55
using CommunityToolkit.WinUI.Notifications;
6+
using Files.App.Helpers;
67
using Files.App.Services.DateTimeFormatter;
78
using Files.App.Services.Settings;
89
using Files.App.Storage.FtpStorage;
@@ -21,6 +22,8 @@
2122
using Microsoft.Extensions.Logging;
2223
using Microsoft.UI.Xaml;
2324
using Microsoft.UI.Xaml.Controls;
25+
using Microsoft.UI.Xaml.Media;
26+
using Microsoft.UI.Xaml.Media.Animation;
2427
using Microsoft.Windows.AppLifecycle;
2528
using System.IO;
2629
using System.Text;
@@ -40,7 +43,7 @@ public partial class App : Application
4043
private static bool ShowErrorNotification = false;
4144
public static string OutputPath { get; set; }
4245
public static CommandBarFlyout? LastOpenedFlyout { get; set; }
43-
public static TaskCompletionSource? SplashScreenLoadingTCS { get; set; }
46+
public static TaskCompletionSource? SplashScreenLoadingTCS { get; private set; }
4447

4548
public static StorageHistoryWrapper HistoryWrapper { get; } = new();
4649
public static AppModel AppModel { get; private set; }
@@ -176,12 +179,7 @@ await Task.WhenAll(
176179
FileTagsHelper.UpdateTagsDb();
177180
});
178181

179-
// Check for required updates
180-
var updateService = Ioc.Default.GetRequiredService<IUpdateService>();
181-
await updateService.CheckForUpdates();
182-
await updateService.DownloadMandatoryUpdates();
183-
await updateService.CheckAndUpdateFilesLauncherAsync();
184-
await updateService.CheckLatestReleaseNotesAsync();
182+
await CheckForRequiredUpdates();
185183

186184
static async Task OptionalTask(Task task, bool condition)
187185
{
@@ -190,6 +188,15 @@ static async Task OptionalTask(Task task, bool condition)
190188
}
191189
}
192190

191+
private static async Task CheckForRequiredUpdates()
192+
{
193+
var updateService = Ioc.Default.GetRequiredService<IUpdateService>();
194+
await updateService.CheckForUpdates();
195+
await updateService.DownloadMandatoryUpdates();
196+
await updateService.CheckAndUpdateFilesLauncherAsync();
197+
await updateService.CheckLatestReleaseNotesAsync();
198+
}
199+
193200
/// <summary>
194201
/// Invoked when the application is launched normally by the end user. Other entry points
195202
/// will be used such as when the application is launched to open a specific file.
@@ -300,6 +307,43 @@ private async void Window_Closed(object sender, WindowEventArgs args)
300307
return;
301308
}
302309

310+
if (Ioc.Default.GetRequiredService<IUserSettingsService>().GeneralSettingsService.LeaveAppRunning &&
311+
!Process.GetProcessesByName("Files").Any(x => x.Id != Process.GetCurrentProcess().Id))
312+
{
313+
// Close open content dialogs
314+
UIHelpers.CloseAllDialogs();
315+
316+
// Cache the window instead of closing it
317+
MainWindow.Instance.AppWindow.Hide();
318+
args.Handled = true;
319+
320+
// Save and close all tabs
321+
SaveSessionTabs();
322+
MainPageViewModel.AppInstances.ForEach(tabItem => tabItem.Unload());
323+
MainPageViewModel.AppInstances.Clear();
324+
await Task.Delay(100);
325+
326+
// Wait for all properties windows to close
327+
await FilePropertiesHelpers.WaitClosingAll();
328+
329+
// Sleep current instance
330+
Program.Pool = new(0, 1, "Files-Instance");
331+
Thread.Yield();
332+
if (Program.Pool.WaitOne())
333+
{
334+
// Resume the instance
335+
Program.Pool.Dispose();
336+
MainWindow.Instance.AppWindow.Show();
337+
MainWindow.Instance.Activate();
338+
339+
_ = CheckForRequiredUpdates();
340+
341+
MainWindow.Instance.EnsureWindowIsInitialized().Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo());
342+
}
343+
344+
return;
345+
}
346+
303347
// Method can take a long time, make sure the window is hidden
304348
await Task.Yield();
305349

src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
// Copyright (c) 2023 Files Community
22
// Licensed under the MIT License. See the LICENSE.
33

4-
using CommunityToolkit.Mvvm.ComponentModel;
54
using Files.App.UserControls.MultitaskingControl;
6-
using Files.App.ViewModels;
75
using Microsoft.UI.Xaml;
86
using Microsoft.UI.Xaml.Input;
97
using System.Collections.Specialized;
10-
using System.ComponentModel;
118

129
namespace Files.App.Data.Contexts
1310
{
@@ -46,7 +43,8 @@ private void AppInstances_CollectionChanged(object? sender, NotifyCollectionChan
4643
}
4744
private void AppModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
4845
{
49-
UpdateCurrentTabIndex();
46+
if (e.PropertyName is nameof(AppModel.TabStripSelectedIndex))
47+
UpdateCurrentTabIndex();
5048
}
5149
private void BaseMultitaskingControl_OnLoaded(object? sender, IMultitaskingControl control)
5250
{

src/Files.App/Data/Models/AppModel.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,13 @@ public int TabStripSelectedIndex
3333
get => tabStripSelectedIndex;
3434
set
3535
{
36-
if (value >= 0)
37-
{
38-
if (tabStripSelectedIndex != value)
39-
{
40-
SetProperty(ref tabStripSelectedIndex, value);
41-
}
36+
SetProperty(ref tabStripSelectedIndex, value);
4237

43-
if (value < MainPageViewModel.AppInstances.Count)
44-
{
45-
Frame rootFrame = (Frame)MainWindow.Instance.Content;
46-
var mainView = (MainPage)rootFrame.Content;
47-
mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value];
48-
}
38+
if (value >= 0 && value < MainPageViewModel.AppInstances.Count)
39+
{
40+
Frame rootFrame = (Frame)MainWindow.Instance.Content;
41+
var mainView = (MainPage)rootFrame.Content;
42+
mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value];
4943
}
5044
}
5145
}

src/Files.App/Helpers/UI/UIHelpers.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,10 @@
22
// Licensed under the MIT License. See the LICENSE.
33

44
using CommunityToolkit.WinUI.Notifications;
5-
using Files.App.Extensions;
6-
using Files.App.Utils.Shell;
7-
using Files.Core.ViewModels.Dialogs;
8-
using Files.Shared;
95
using Microsoft.UI.Xaml.Controls;
106
using Microsoft.UI.Xaml.Media;
117
using Microsoft.UI.Xaml.Media.Imaging;
12-
using System;
13-
using System.Collections.Generic;
14-
using System.ComponentModel;
15-
using System.Diagnostics;
168
using System.IO;
17-
using System.Linq;
18-
using System.Threading.Tasks;
199
using Windows.UI.Notifications;
2010

2111
namespace Files.App.Helpers
@@ -130,7 +120,7 @@ private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog)
130120

131121
public static void CloseAllDialogs()
132122
{
133-
var openedDialogs = VisualTreeHelper.GetOpenPopups(MainWindow.Instance);
123+
var openedDialogs = VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot);
134124

135125
foreach (var item in openedDialogs)
136126
{

src/Files.App/MainWindow.xaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
using Files.App.UserControls.MultitaskingControl;
55
using Microsoft.UI;
66
using Microsoft.UI.Windowing;
7+
using Microsoft.UI.Xaml;
78
using Microsoft.UI.Xaml.Controls;
89
using Microsoft.UI.Xaml.Media.Animation;
910
using Microsoft.UI.Xaml.Navigation;
1011
using System.IO;
11-
using System.Runtime.InteropServices;
1212
using Windows.ApplicationModel;
1313
using Windows.ApplicationModel.Activation;
1414
using Windows.Storage;
@@ -176,7 +176,7 @@ public async Task InitializeApplication(object activatedEventArgs)
176176
rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo());
177177
}
178178

179-
private Frame EnsureWindowIsInitialized()
179+
public Frame EnsureWindowIsInitialized()
180180
{
181181
// NOTE:
182182
// Do not repeat app initialization when the Window already has content,

src/Files.App/Program.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ namespace Files.App
1414
{
1515
internal class Program
1616
{
17+
public static Semaphore Pool;
18+
19+
static Program()
20+
{
21+
Pool = new(0, 1, "Files-Instance", out var isNew);
22+
if (!isNew)
23+
{
24+
// Resume cached instance
25+
Pool.Release();
26+
Environment.Exit(0);
27+
}
28+
Pool.Dispose();
29+
}
30+
1731
// Note:
1832
// We can't declare Main to be async because in a WinUI app
1933
// This prevents Narrator from reading XAML elements

src/Files.App/Services/Settings/GeneralSettingsService.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ public bool ShowOpenInNewPane
208208
set => Set(value);
209209
}
210210

211+
public bool LeaveAppRunning
212+
{
213+
get => Get(true);
214+
set => Set(value);
215+
}
216+
211217
public FileNameConflictResolveOptionType ConflictsResolveOption
212218
{
213219
get => (FileNameConflictResolveOptionType)Get((long)FileNameConflictResolveOptionType.GenerateNewName);
@@ -254,6 +260,7 @@ protected override void RaiseOnSettingChangedEvent(object sender, SettingChanged
254260
case nameof(ShowOpenInNewTab):
255261
case nameof(ShowOpenInNewWindow):
256262
case nameof(ShowOpenInNewPane):
263+
case nameof(LeaveAppRunning):
257264
case nameof(ConflictsResolveOption):
258265
case nameof(ShowHashesDictionary):
259266
Analytics.TrackEvent($"Set {e.SettingName} to {e.NewValue}");

src/Files.App/Strings/en-US/Resources.resw

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3447,4 +3447,7 @@
34473447
<data name="FilesRunningAsAdminContent" xml:space="preserve">
34483448
<value>Due to platform limitations, drag and drop isn't available when running Files as administrator.</value>
34493449
</data>
3450+
<data name="SettingsLeaveAppRunning" xml:space="preserve">
3451+
<value>Leave app running in the background when the window is closed</value>
3452+
</data>
34503453
</root>

src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public static class FilePropertiesHelpers
3131
public static nint GetWindowHandle(Window w)
3232
=> WinRT.Interop.WindowNative.GetWindowHandle(w);
3333

34+
private static int WindowCount = 0;
35+
private static TaskCompletionSource? PropertiesWindowsClosingTCS;
3436
private static BlockingCollection<WinUIEx.WindowEx> WindowCache = new();
3537

3638
/// <summary>
@@ -140,8 +142,10 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan
140142
+ Math.Max(0, Math.Min(displayArea.WorkArea.Height - appWindow.Size.Height, pointerPosition.Y - displayArea.WorkArea.Y)),
141143
};
142144

143-
appWindow.Move(appWindowPos);
145+
if (Interlocked.Increment(ref WindowCount) == 1)
146+
PropertiesWindowsClosingTCS = new();
144147

148+
appWindow.Move(appWindowPos);
145149
appWindow.Show();
146150
}
147151

@@ -156,9 +160,19 @@ private static void PropertiesWindow_Closed(object sender, WindowEventArgs args)
156160
window.AppWindow.Hide();
157161
window.Content = null;
158162
WindowCache.Add(window);
163+
164+
if (Interlocked.Decrement(ref WindowCount) == 0)
165+
{
166+
PropertiesWindowsClosingTCS!.TrySetResult();
167+
PropertiesWindowsClosingTCS = null;
168+
}
159169
}
160170
}
161171

172+
/// <summary>
173+
/// Destroy all cached properties windows
174+
/// </summary>
175+
/// <returns></returns>
162176
public static void DestroyCachedWindows()
163177
{
164178
while (WindowCache.TryTake(out var window))
@@ -167,5 +181,11 @@ public static void DestroyCachedWindows()
167181
window.Close();
168182
}
169183
}
184+
185+
/// <summary>
186+
/// Returns task to wait for all properties windows to close
187+
/// </summary>
188+
/// <returns>Task to wait</returns>
189+
public static Task WaitClosingAll() => PropertiesWindowsClosingTCS?.Task ?? Task.CompletedTask;
170190
}
171191
}

src/Files.App/ViewModels/Settings/AdvancedViewModel.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,20 @@ public bool CanOpenOnWindowsStartup
297297
set => SetProperty(ref canOpenOnWindowsStartup, value);
298298
}
299299

300+
public bool LeaveAppRunning
301+
{
302+
get => UserSettingsService.GeneralSettingsService.LeaveAppRunning;
303+
set
304+
{
305+
if (value != UserSettingsService.GeneralSettingsService.LeaveAppRunning)
306+
{
307+
UserSettingsService.GeneralSettingsService.LeaveAppRunning = value;
308+
309+
OnPropertyChanged();
310+
}
311+
}
312+
}
313+
300314
public async Task OpenFilesOnWindowsStartup()
301315
{
302316
var stateMode = await ReadState();

0 commit comments

Comments
 (0)