Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Added an option to keep Files running in the background for quicker startup time #13236

Merged
merged 15 commits into from
Aug 24, 2023
58 changes: 51 additions & 7 deletions src/Files.App/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using CommunityToolkit.WinUI.Helpers;
using CommunityToolkit.WinUI.Notifications;
using Files.App.Helpers;
using Files.App.Services.DateTimeFormatter;
using Files.App.Services.Settings;
using Files.App.Storage.FtpStorage;
Expand All @@ -21,6 +22,8 @@
using Microsoft.Extensions.Logging;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.Windows.AppLifecycle;
using System.IO;
using System.Text;
Expand All @@ -40,7 +43,7 @@ public partial class App : Application
private static bool ShowErrorNotification = false;
public static string OutputPath { get; set; }
public static CommandBarFlyout? LastOpenedFlyout { get; set; }
public static TaskCompletionSource? SplashScreenLoadingTCS { get; set; }
public static TaskCompletionSource? SplashScreenLoadingTCS { get; private set; }

public static StorageHistoryWrapper HistoryWrapper { get; } = new();
public static AppModel AppModel { get; private set; }
Expand Down Expand Up @@ -176,12 +179,7 @@ await Task.WhenAll(
FileTagsHelper.UpdateTagsDb();
});

// Check for required updates
var updateService = Ioc.Default.GetRequiredService<IUpdateService>();
await updateService.CheckForUpdates();
await updateService.DownloadMandatoryUpdates();
await updateService.CheckAndUpdateFilesLauncherAsync();
await updateService.CheckLatestReleaseNotesAsync();
await CheckForRequiredUpdates();

static async Task OptionalTask(Task task, bool condition)
{
Expand All @@ -190,6 +188,15 @@ static async Task OptionalTask(Task task, bool condition)
}
}

private static async Task CheckForRequiredUpdates()
{
var updateService = Ioc.Default.GetRequiredService<IUpdateService>();
await updateService.CheckForUpdates();
await updateService.DownloadMandatoryUpdates();
await updateService.CheckAndUpdateFilesLauncherAsync();
await updateService.CheckLatestReleaseNotesAsync();
}

/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
Expand Down Expand Up @@ -300,6 +307,43 @@ private async void Window_Closed(object sender, WindowEventArgs args)
return;
}

if (Ioc.Default.GetRequiredService<IUserSettingsService>().GeneralSettingsService.LeaveAppRunning &&
!Process.GetProcessesByName("Files").Any(x => x.Id != Process.GetCurrentProcess().Id))
{
// Close open content dialogs
UIHelpers.CloseAllDialogs();

// Cache the window instead of closing it
MainWindow.Instance.AppWindow.Hide();
args.Handled = true;

// Save and close all tabs
SaveSessionTabs();
MainPageViewModel.AppInstances.ForEach(tabItem => tabItem.Unload());
MainPageViewModel.AppInstances.Clear();
await Task.Delay(100);

// Wait for all properties windows to close
hishitetsu marked this conversation as resolved.
Show resolved Hide resolved
await FilePropertiesHelpers.WaitClosingAll();

// Sleep current instance
Program.Pool = new(0, 1, "Files-Instance");
Thread.Yield();
if (Program.Pool.WaitOne())
{
// Resume the instance
Program.Pool.Dispose();
MainWindow.Instance.AppWindow.Show();
MainWindow.Instance.Activate();

_ = CheckForRequiredUpdates();

MainWindow.Instance.EnsureWindowIsInitialized().Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo());
}

return;
}

// Method can take a long time, make sure the window is hidden
await Task.Yield();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.Mvvm.ComponentModel;
using Files.App.UserControls.MultitaskingControl;
using Files.App.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Files.App.Data.Contexts
{
Expand Down Expand Up @@ -46,7 +43,8 @@ private void AppInstances_CollectionChanged(object? sender, NotifyCollectionChan
}
private void AppModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
UpdateCurrentTabIndex();
if (e.PropertyName is nameof(AppModel.TabStripSelectedIndex))
UpdateCurrentTabIndex();
}
private void BaseMultitaskingControl_OnLoaded(object? sender, IMultitaskingControl control)
{
Expand Down
18 changes: 6 additions & 12 deletions src/Files.App/Data/Models/AppModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,13 @@ public int TabStripSelectedIndex
get => tabStripSelectedIndex;
set
{
if (value >= 0)
{
if (tabStripSelectedIndex != value)
{
SetProperty(ref tabStripSelectedIndex, value);
}
SetProperty(ref tabStripSelectedIndex, value);

if (value < MainPageViewModel.AppInstances.Count)
{
Frame rootFrame = (Frame)MainWindow.Instance.Content;
var mainView = (MainPage)rootFrame.Content;
mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value];
}
if (value >= 0 && value < MainPageViewModel.AppInstances.Count)
{
Frame rootFrame = (Frame)MainWindow.Instance.Content;
var mainView = (MainPage)rootFrame.Content;
mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value];
}
}
}
Expand Down
12 changes: 1 addition & 11 deletions src/Files.App/Helpers/UI/UIHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,10 @@
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.WinUI.Notifications;
using Files.App.Extensions;
using Files.App.Utils.Shell;
using Files.Core.ViewModels.Dialogs;
using Files.Shared;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.UI.Notifications;

namespace Files.App.Helpers
Expand Down Expand Up @@ -130,7 +120,7 @@ private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog)

public static void CloseAllDialogs()
{
var openedDialogs = VisualTreeHelper.GetOpenPopups(MainWindow.Instance);
var openedDialogs = VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot);

foreach (var item in openedDialogs)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Files.App/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
using Files.App.UserControls.MultitaskingControl;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation;
using System.IO;
using System.Runtime.InteropServices;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
Expand Down Expand Up @@ -176,7 +176,7 @@ public async Task InitializeApplication(object activatedEventArgs)
rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo());
}

private Frame EnsureWindowIsInitialized()
public Frame EnsureWindowIsInitialized()
{
// NOTE:
// Do not repeat app initialization when the Window already has content,
Expand Down
14 changes: 14 additions & 0 deletions src/Files.App/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ namespace Files.App
{
internal class Program
{
public static Semaphore Pool;

static Program()
{
Pool = new(0, 1, "Files-Instance", out var isNew);
if (!isNew)
{
// Resume cached instance
Pool.Release();
Environment.Exit(0);
}
Pool.Dispose();
}

// Note:
// We can't declare Main to be async because in a WinUI app
// This prevents Narrator from reading XAML elements
Expand Down
7 changes: 7 additions & 0 deletions src/Files.App/Services/Settings/GeneralSettingsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ public bool ShowOpenInNewPane
set => Set(value);
}

public bool LeaveAppRunning
{
get => Get(true);
set => Set(value);
}

public FileNameConflictResolveOptionType ConflictsResolveOption
{
get => (FileNameConflictResolveOptionType)Get((long)FileNameConflictResolveOptionType.GenerateNewName);
Expand Down Expand Up @@ -254,6 +260,7 @@ protected override void RaiseOnSettingChangedEvent(object sender, SettingChanged
case nameof(ShowOpenInNewTab):
case nameof(ShowOpenInNewWindow):
case nameof(ShowOpenInNewPane):
case nameof(LeaveAppRunning):
case nameof(ConflictsResolveOption):
case nameof(ShowHashesDictionary):
Analytics.TrackEvent($"Set {e.SettingName} to {e.NewValue}");
Expand Down
3 changes: 3 additions & 0 deletions src/Files.App/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -3447,4 +3447,7 @@
<data name="FilesRunningAsAdminContent" xml:space="preserve">
<value>Due to platform limitations, drag and drop isn't available when running Files as administrator.</value>
</data>
<data name="SettingsLeaveAppRunning" xml:space="preserve">
<value>Leave app running in the background when the window is closed</value>
</data>
</root>
22 changes: 21 additions & 1 deletion src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public static class FilePropertiesHelpers
public static nint GetWindowHandle(Window w)
=> WinRT.Interop.WindowNative.GetWindowHandle(w);

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

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

appWindow.Move(appWindowPos);
if (Interlocked.Increment(ref WindowCount) == 1)
PropertiesWindowsClosingTCS = new();

appWindow.Move(appWindowPos);
appWindow.Show();
}

Expand All @@ -156,9 +160,19 @@ private static void PropertiesWindow_Closed(object sender, WindowEventArgs args)
window.AppWindow.Hide();
window.Content = null;
WindowCache.Add(window);

if (Interlocked.Decrement(ref WindowCount) == 0)
{
PropertiesWindowsClosingTCS!.TrySetResult();
PropertiesWindowsClosingTCS = null;
}
}
}

/// <summary>
/// Destroy all cached properties windows
/// </summary>
/// <returns></returns>
public static void DestroyCachedWindows()
{
while (WindowCache.TryTake(out var window))
Expand All @@ -167,5 +181,11 @@ public static void DestroyCachedWindows()
window.Close();
}
}

/// <summary>
/// Returns task to wait for all properties windows to close
/// </summary>
/// <returns>Task to wait</returns>
public static Task WaitClosingAll() => PropertiesWindowsClosingTCS?.Task ?? Task.CompletedTask;
}
}
14 changes: 14 additions & 0 deletions src/Files.App/ViewModels/Settings/AdvancedViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,20 @@ public bool CanOpenOnWindowsStartup
set => SetProperty(ref canOpenOnWindowsStartup, value);
}

public bool LeaveAppRunning
{
get => UserSettingsService.GeneralSettingsService.LeaveAppRunning;
set
{
if (value != UserSettingsService.GeneralSettingsService.LeaveAppRunning)
{
UserSettingsService.GeneralSettingsService.LeaveAppRunning = value;

OnPropertyChanged();
}
}
}

public async Task OpenFilesOnWindowsStartup()
{
var stateMode = await ReadState();
Expand Down
15 changes: 5 additions & 10 deletions src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
using Files.App.Data.Parameters;
using Files.App.Helpers;
using Files.App.ViewModels;
using Files.App.ViewModels.Properties;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Threading.Tasks;
using Windows.System;
using Windows.UI;

Expand Down Expand Up @@ -45,16 +38,15 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
AppWindow = parameter.AppWindow;
Window = parameter.Window;

AppSettings = Ioc.Default.GetRequiredService<SettingsViewModel>();
AppSettings.ThemeModeChanged += AppSettings_ThemeModeChanged;

base.OnNavigatedTo(e);

MainPropertiesViewModel = new(Window, AppWindow, MainContentFrame, BaseProperties, parameter);
}

private void Page_Loaded(object sender, RoutedEventArgs e)
{
AppSettings = Ioc.Default.GetRequiredService<SettingsViewModel>();
AppSettings.ThemeModeChanged += AppSettings_ThemeModeChanged;
Window.Closed += Window_Closed;

UpdatePageLayout();
Expand Down Expand Up @@ -89,6 +81,9 @@ private void UpdatePageLayout()

private async void AppSettings_ThemeModeChanged(object? sender, EventArgs e)
{
if (Parent is null)
return;

await DispatcherQueue.EnqueueOrInvokeAsync(() =>
{
((Frame)Parent).RequestedTheme = ThemeHelper.RootTheme;
Expand Down
11 changes: 11 additions & 0 deletions src/Files.App/Views/Settings/AdvancedPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@
</ToggleSwitch>
</local:SettingsBlockControl>

<!-- Leave App Running -->
<local:SettingsBlockControl Title="{helpers:ResourceString Name=SettingsLeaveAppRunning}" HorizontalAlignment="Stretch">
yaira2 marked this conversation as resolved.
Show resolved Hide resolved
<local:SettingsBlockControl.Icon>
<FontIcon Glyph="&#xE8E6;" />
</local:SettingsBlockControl.Icon>
<ToggleSwitch
AutomationProperties.Name="{helpers:ResourceString Name=SettingsLeaveAppRunning}"
IsOn="{x:Bind ViewModel.LeaveAppRunning, Mode=TwoWay}"
Style="{StaticResource RightAlignedToggleSwitchStyle}" />
</local:SettingsBlockControl>

<!-- Experimental Settings -->
<TextBlock
Padding="0,16,0,4"
Expand Down
Loading