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

Support Logon Task for Faster Startup Experience #3218

Merged
merged 8 commits into from
Feb 25, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Flow.Launcher.Infrastructure/UserSettings/Settings.cs
Original file line number Diff line number Diff line change
@@ -238,6 +238,7 @@ public SearchPrecisionScore QuerySearchPrecision
public bool EnableUpdateLog { get; set; }

public bool StartFlowLauncherOnSystemStartup { get; set; } = false;
public bool UseLogonTaskForStartup { get; set; } = false;
public bool HideOnStartup { get; set; } = true;
bool _hideNotifyIcon { get; set; }
public bool HideNotifyIcon
9 changes: 8 additions & 1 deletion Flow.Launcher/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -119,7 +119,14 @@ private void AutoStartup()
{
try
{
Helper.AutoStartup.Enable();
if (_settings.UseLogonTaskForStartup)
{
Helper.AutoStartup.EnableViaLogonTask();
}
else
{
Helper.AutoStartup.EnableViaRegistry();
}
}
catch (Exception e)
{
1 change: 1 addition & 0 deletions Flow.Launcher/Flow.Launcher.csproj
Original file line number Diff line number Diff line change
@@ -100,6 +100,7 @@
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
<PackageReference Include="TaskScheduler" Version="2.11.0" />
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
</ItemGroup>

144 changes: 138 additions & 6 deletions Flow.Launcher/Helper/AutoStartup.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
using System;
using System.IO;
using System.Linq;
using System.Security.Principal;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Microsoft.Win32;
using Microsoft.Win32.TaskScheduler;

namespace Flow.Launcher.Helper;

public class AutoStartup
{
private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";

public static bool IsEnabled
{
get
{
// Check if logon task is enabled
if (CheckLogonTask())
{
return true;
}

// Check if registry is enabled
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
@@ -28,12 +41,74 @@ public static bool IsEnabled
}
}

public static void Disable()
private static bool CheckLogonTask()
{
using var taskService = new TaskService();
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
if (task != null)
{
try
{
// Check if the action is the same as the current executable path
var action = task.Definition.Actions.FirstOrDefault()!.ToString().Trim();
if (!Constant.ExecutablePath.Equals(action, StringComparison.OrdinalIgnoreCase) && !File.Exists(action))
{
UnscheduleLogonTask();
ScheduleLogonTask();
}

return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to check logon task: {e}");
}
}

return false;
}

public static void DisableViaLogonTaskAndRegistry()
{
Disable(true);
Disable(false);
}

public static void EnableViaLogonTask()
{
Enable(true);
}

public static void EnableViaRegistry()
{
Enable(false);
}

public static void ChangeToViaLogonTask()
{
Disable(false);
Enable(true);
}

public static void ChangeToViaRegistry()
{
Disable(true);
Enable(false);
}

private static void Disable(bool logonTask)
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.DeleteValue(Constant.FlowLauncher, false);
if (logonTask)
{
UnscheduleLogonTask();
}
else
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.DeleteValue(Constant.FlowLauncher, false);
}
}
catch (Exception e)
{
@@ -42,17 +117,74 @@ public static void Disable()
}
}

internal static void Enable()
private static void Enable(bool logonTask)
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
if (logonTask)
{
ScheduleLogonTask();
}
else
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
}
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}");
throw;
}
}

private static bool ScheduleLogonTask()
{
using var td = TaskService.Instance.NewTask();
td.RegistrationInfo.Description = LogonTaskDesc;
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
td.Actions.Add(Constant.ExecutablePath);

if (IsCurrentUserIsAdmin())
{
td.Principal.RunLevel = TaskRunLevel.Highest;
}

td.Settings.StopIfGoingOnBatteries = false;
td.Settings.DisallowStartIfOnBatteries = false;
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;

try
{
TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td);
return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to schedule logon task: {e}");
return false;
}
}

private static bool UnscheduleLogonTask()
{
using var taskService = new TaskService();
try
{
taskService.RootFolder.DeleteTask(LogonTaskName);
return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}");
return false;
}
}

private static bool IsCurrentUserIsAdmin()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
}
2 changes: 2 additions & 0 deletions Flow.Launcher/Languages/en.xaml
Original file line number Diff line number Diff line change
@@ -46,6 +46,8 @@
<system:String x:Key="portableMode">Portable Mode</system:String>
<system:String x:Key="portableModeToolTIp">Store all settings and user data in one folder (Useful when used with removable drives or cloud services).</system:String>
<system:String x:Key="startFlowLauncherOnSystemStartup">Start Flow Launcher on system startup</system:String>
<system:String x:Key="useLogonTaskForStartup">Use logon task instead of startup entry for faster startup experience</system:String>
<system:String x:Key="useLogonTaskForStartupTooltip">After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler</system:String>
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Hide Flow Launcher when focus is lost</system:String>
<system:String x:Key="dontPromptUpdateMsg">Do not show new version notifications</system:String>
Original file line number Diff line number Diff line change
@@ -42,9 +42,20 @@ public bool StartFlowLauncherOnSystemStartup
try
{
if (value)
AutoStartup.Enable();
{
if (UseLogonTaskForStartup)
{
AutoStartup.EnableViaLogonTask();
}
else
{
AutoStartup.EnableViaRegistry();
}
}
else
AutoStartup.Disable();
{
AutoStartup.DisableViaLogonTaskAndRegistry();
}
}
catch (Exception e)
{
@@ -54,6 +65,34 @@ public bool StartFlowLauncherOnSystemStartup
}
}

public bool UseLogonTaskForStartup
{
get => Settings.UseLogonTaskForStartup;
set
{
Settings.UseLogonTaskForStartup = value;

if (StartFlowLauncherOnSystemStartup)
{
try
{
if (UseLogonTaskForStartup)
{
AutoStartup.ChangeToViaLogonTask();
}
else
{
AutoStartup.ChangeToViaRegistry();
}
}
catch (Exception e)
{
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
e.Message);
}
}
}
}

public List<SearchWindowScreenData> SearchWindowScreens { get; } =
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");
7 changes: 7 additions & 0 deletions Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
Original file line number Diff line number Diff line change
@@ -36,6 +36,13 @@
OnContent="{DynamicResource enable}" />
</cc:Card>

<cc:Card Title="{DynamicResource useLogonTaskForStartup}" Sub="{DynamicResource useLogonTaskForStartupTooltip}">
<ui:ToggleSwitch
IsOn="{Binding UseLogonTaskForStartup}"
OffContent="{DynamicResource disable}"
OnContent="{DynamicResource enable}" />
</cc:Card>

<cc:Card
Title="{DynamicResource hideOnStartup}"
Icon="&#xed1a;"

Unchanged files with check annotations Beta

pythonw
dotnet
winget
jjw24

Check failure on line 37 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`2` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)

Check failure on line 37 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`4` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)
wolframalpha
gmail
duckduckgo
EWX
dlgtext
CMD
appref-ms

Check failure on line 52 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`-` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)
appref
TSource
runas
popup
ptr
pluginindicator
TobiasSekan

Check failure on line 60 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`TobiasS` matches a line_forbidden.patterns entry: `[A-Z](?:[A-Z]|[a-z]|')*?[a-z](?:[a-z]|')*[A-Z]`. (forbidden-pattern)
img
resx
bak
ddd
dddd
clearlogfolder
ACCENT_ENABLE_TRANSPARENTGRADIENT

Check failure on line 71 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`_` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)

Check failure on line 71 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`_` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)
ACCENT_ENABLE_BLURBEHIND

Check failure on line 72 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`_` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)

Check failure on line 72 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`_` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)
WCA_ACCENT_POLICY

Check failure on line 73 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`_` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)

Check failure on line 73 in .github/actions/spelling/expect.txt

GitHub Actions / Check Spelling

`_` matches a line_forbidden.patterns entry: `(?![A-Z]|[a-z]|'|\s|=).`. (forbidden-pattern)
HGlobal
dopusrt
firefox