Skip to content

Commit 3260fab

Browse files
committed
Add support for changing startup to logon task for faster startup experience
1 parent 6cf7674 commit 3260fab

File tree

6 files changed

+151
-9
lines changed

6 files changed

+151
-9
lines changed

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ public SearchPrecisionScore QuerySearchPrecision
238238
public bool EnableUpdateLog { get; set; }
239239

240240
public bool StartFlowLauncherOnSystemStartup { get; set; } = false;
241+
public bool UseLogonTaskForStartup { get; set; } = false;
241242
public bool HideOnStartup { get; set; } = true;
242243
bool _hideNotifyIcon { get; set; }
243244
public bool HideNotifyIcon

Flow.Launcher/App.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private void AutoStartup()
119119
{
120120
try
121121
{
122-
Helper.AutoStartup.Enable();
122+
Helper.AutoStartup.Enable(_settings.UseLogonTaskForStartup);
123123
}
124124
catch (Exception e)
125125
{

Flow.Launcher/Flow.Launcher.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
101101
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
102102
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
103+
<PackageReference Include="TaskScheduler" Version="2.11.0" />
103104
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.0" />
104105
</ItemGroup>
105106

Flow.Launcher/Helper/AutoStartup.cs

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Security.Principal;
25
using Flow.Launcher.Infrastructure;
36
using Flow.Launcher.Infrastructure.Logger;
47
using Microsoft.Win32;
8+
using Microsoft.Win32.TaskScheduler;
59

610
namespace Flow.Launcher.Helper;
711

812
public class AutoStartup
913
{
1014
private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
15+
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
16+
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
1117

1218
public static bool IsEnabled
1319
{
1420
get
1521
{
22+
// Check if logon task is enabled
23+
if (CheckLogonTask())
24+
{
25+
return true;
26+
}
27+
28+
// Check if registry is enabled
1629
try
1730
{
1831
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
@@ -28,12 +41,45 @@ public static bool IsEnabled
2841
}
2942
}
3043

31-
public static void Disable()
44+
private static bool CheckLogonTask()
45+
{
46+
using var taskService = new TaskService();
47+
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
48+
if (task != null)
49+
{
50+
try
51+
{
52+
// Check if the action is the same as the current executable path
53+
var action = task.Definition.Actions.FirstOrDefault()!.ToString().Trim();
54+
if (!Constant.ExecutablePath.Equals(action, StringComparison.OrdinalIgnoreCase) && !File.Exists(action))
55+
{
56+
UnscheduleLogonTask();
57+
ScheduleLogonTask();
58+
}
59+
}
60+
catch (Exception)
61+
{
62+
Log.Error("AutoStartup", "Failed to check logon task");
63+
return false;
64+
}
65+
}
66+
67+
return true;
68+
}
69+
70+
public static void Disable(bool logonTask)
3271
{
3372
try
3473
{
35-
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
36-
key?.DeleteValue(Constant.FlowLauncher, false);
74+
if (logonTask)
75+
{
76+
UnscheduleLogonTask();
77+
}
78+
else
79+
{
80+
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
81+
key?.DeleteValue(Constant.FlowLauncher, false);
82+
}
3783
}
3884
catch (Exception e)
3985
{
@@ -42,17 +88,74 @@ public static void Disable()
4288
}
4389
}
4490

45-
internal static void Enable()
91+
internal static void Enable(bool logonTask)
4692
{
4793
try
4894
{
49-
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
50-
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
95+
if (logonTask)
96+
{
97+
ScheduleLogonTask();
98+
}
99+
else
100+
{
101+
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
102+
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
103+
}
51104
}
52105
catch (Exception e)
53106
{
54107
Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}");
55108
throw;
56109
}
57110
}
111+
112+
private static bool ScheduleLogonTask()
113+
{
114+
using var td = TaskService.Instance.NewTask();
115+
td.RegistrationInfo.Description = LogonTaskDesc;
116+
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
117+
td.Actions.Add(Constant.ExecutablePath);
118+
119+
if (IsCurrentUserIsAdmin())
120+
{
121+
td.Principal.RunLevel = TaskRunLevel.Highest;
122+
}
123+
124+
td.Settings.StopIfGoingOnBatteries = false;
125+
td.Settings.DisallowStartIfOnBatteries = false;
126+
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;
127+
128+
try
129+
{
130+
TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td);
131+
return true;
132+
}
133+
catch (Exception)
134+
{
135+
Log.Error("AutoStartup", "Failed to schedule logon task");
136+
return false;
137+
}
138+
}
139+
140+
private static bool UnscheduleLogonTask()
141+
{
142+
using var taskService = new TaskService();
143+
try
144+
{
145+
taskService.RootFolder.DeleteTask(LogonTaskName);
146+
return true;
147+
}
148+
catch (Exception)
149+
{
150+
Log.Error("AutoStartup", "Failed to unschedule logon task");
151+
return false;
152+
}
153+
}
154+
155+
private static bool IsCurrentUserIsAdmin()
156+
{
157+
var identity = WindowsIdentity.GetCurrent();
158+
var principal = new WindowsPrincipal(identity);
159+
return principal.IsInRole(WindowsBuiltInRole.Administrator);
160+
}
58161
}

Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,16 @@ public bool StartFlowLauncherOnSystemStartup
4242
try
4343
{
4444
if (value)
45-
AutoStartup.Enable();
45+
{
46+
// Enable either registry or task scheduler
47+
AutoStartup.Enable(UseLogonTaskForStartup);
48+
}
4649
else
47-
AutoStartup.Disable();
50+
{
51+
// Disable both registry and task scheduler
52+
AutoStartup.Disable(true);
53+
AutoStartup.Disable(false);
54+
}
4855
}
4956
catch (Exception e)
5057
{
@@ -54,6 +61,29 @@ public bool StartFlowLauncherOnSystemStartup
5461
}
5562
}
5663

64+
public bool UseLogonTaskForStartup
65+
{
66+
get => Settings.UseLogonTaskForStartup;
67+
set
68+
{
69+
Settings.UseLogonTaskForStartup = value;
70+
71+
if (StartFlowLauncherOnSystemStartup)
72+
{
73+
try
74+
{
75+
// Disable and enable to update the startup method
76+
AutoStartup.Disable(!UseLogonTaskForStartup);
77+
AutoStartup.Enable(UseLogonTaskForStartup);
78+
}
79+
catch (Exception e)
80+
{
81+
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
82+
e.Message);
83+
}
84+
}
85+
}
86+
}
5787

5888
public List<SearchWindowScreenData> SearchWindowScreens { get; } =
5989
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");

Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
OnContent="{DynamicResource enable}" />
3737
</cc:Card>
3838

39+
<cc:Card Title="Use logon task instead of startup entry for faster startup experience" Icon="&#xe8fc;">
40+
<ui:ToggleSwitch
41+
IsOn="{Binding UseLogonTaskForStartup}"
42+
OffContent="{DynamicResource disable}"
43+
OnContent="{DynamicResource enable}" />
44+
</cc:Card>
45+
3946
<cc:Card
4047
Title="{DynamicResource hideOnStartup}"
4148
Icon="&#xed1a;"

0 commit comments

Comments
 (0)