Skip to content

Commit 4f46a8c

Browse files
authored
Merge branch 'dev' into settings_panel
2 parents 51a9999 + 79d54d0 commit 4f46a8c

28 files changed

+504
-144
lines changed

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ public static class PluginManager
2929
public static readonly HashSet<PluginPair> GlobalPlugins = new();
3030
public static readonly Dictionary<string, PluginPair> NonGlobalPlugins = new();
3131

32-
public static IPublicAPI API { get; private set; } = Ioc.Default.GetRequiredService<IPublicAPI>();
32+
// We should not initialize API in static constructor because it will create another API instance
33+
private static IPublicAPI api = null;
34+
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
3335

3436
private static PluginsSettings Settings;
3537
private static List<PluginMetadata> _metadatas;

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

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

258258
public bool StartFlowLauncherOnSystemStartup { get; set; } = false;
259+
public bool UseLogonTaskForStartup { get; set; } = false;
259260
public bool HideOnStartup { get; set; } = true;
260261
bool _hideNotifyIcon { get; set; }
261262
public bool HideNotifyIcon

Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -323,17 +323,16 @@ public interface IPublicAPI
323323
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK);
324324

325325
/// <summary>
326-
/// Displays a standardised Flow message box.
327-
/// If there is issue when showing the message box, it will return null.
326+
/// Displays a standardised Flow progress box.
328327
/// </summary>
329-
/// <param name="caption">The caption of the message box.</param>
328+
/// <param name="caption">The caption of the progress box.</param>
330329
/// <param name="reportProgressAsync">
331330
/// Time-consuming task function, whose input is the action to report progress.
332331
/// The input of the action is the progress value which is a double value between 0 and 100.
333332
/// If there are any exceptions, this action will be null.
334333
/// </param>
335-
/// <param name="forceClosed">When user closes the progress box manually by button or esc key, this action will be called.</param>
336-
/// <returns>A progress box interface.</returns>
337-
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null);
334+
/// <param name="cancelProgress">When user cancel the progress, this action will be called.</param>
335+
/// <returns></returns>
336+
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action cancelProgress = null);
338337
}
339338
}

Flow.Launcher/App.xaml.cs

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,31 +35,64 @@ public partial class App : IDisposable, ISingleInstanceApp
3535
public App()
3636
{
3737
// Initialize settings
38-
var storage = new FlowLauncherJsonStorage<Settings>();
39-
_settings = storage.Load();
40-
_settings.SetStorage(storage);
41-
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
38+
try
39+
{
40+
var storage = new FlowLauncherJsonStorage<Settings>();
41+
_settings = storage.Load();
42+
_settings.SetStorage(storage);
43+
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
44+
}
45+
catch (Exception e)
46+
{
47+
ShowErrorMsgBoxAndFailFast("Cannot load setting storage, please check local data directory", e);
48+
return;
49+
}
4250

4351
// Configure the dependency injection container
44-
var host = Host.CreateDefaultBuilder()
45-
.UseContentRoot(AppContext.BaseDirectory)
46-
.ConfigureServices(services => services
47-
.AddSingleton(_ => _settings)
48-
.AddSingleton(sp => new Updater(sp.GetRequiredService<IPublicAPI>(), Launcher.Properties.Settings.Default.GithubRepo))
49-
.AddSingleton<Portable>()
50-
.AddSingleton<SettingWindowViewModel>()
51-
.AddSingleton<IAlphabet, PinyinAlphabet>()
52-
.AddSingleton<StringMatcher>()
53-
.AddSingleton<Internationalization>()
54-
.AddSingleton<IPublicAPI, PublicAPIInstance>()
55-
.AddSingleton<MainViewModel>()
56-
.AddSingleton<Theme>()
57-
).Build();
58-
Ioc.Default.ConfigureServices(host.Services);
52+
try
53+
{
54+
var host = Host.CreateDefaultBuilder()
55+
.UseContentRoot(AppContext.BaseDirectory)
56+
.ConfigureServices(services => services
57+
.AddSingleton(_ => _settings)
58+
.AddSingleton(sp => new Updater(sp.GetRequiredService<IPublicAPI>(), Launcher.Properties.Settings.Default.GithubRepo))
59+
.AddSingleton<Portable>()
60+
.AddSingleton<SettingWindowViewModel>()
61+
.AddSingleton<IAlphabet, PinyinAlphabet>()
62+
.AddSingleton<StringMatcher>()
63+
.AddSingleton<Internationalization>()
64+
.AddSingleton<IPublicAPI, PublicAPIInstance>()
65+
.AddSingleton<MainViewModel>()
66+
.AddSingleton<Theme>()
67+
).Build();
68+
Ioc.Default.ConfigureServices(host.Services);
69+
}
70+
catch (Exception e)
71+
{
72+
ShowErrorMsgBoxAndFailFast("Cannot configure dependency injection container, please open new issue in Flow.Launcher", e);
73+
return;
74+
}
5975

6076
// Initialize the public API and Settings first
61-
API = Ioc.Default.GetRequiredService<IPublicAPI>();
62-
_settings.Initialize();
77+
try
78+
{
79+
API = Ioc.Default.GetRequiredService<IPublicAPI>();
80+
_settings.Initialize();
81+
}
82+
catch (Exception e)
83+
{
84+
ShowErrorMsgBoxAndFailFast("Cannot initialize api and settings, please open new issue in Flow.Launcher", e);
85+
return;
86+
}
87+
}
88+
89+
private static void ShowErrorMsgBoxAndFailFast(string message, Exception e)
90+
{
91+
// Firstly show users the message
92+
MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error);
93+
94+
// Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
95+
Environment.FailFast(message, e);
6396
}
6497

6598
[STAThread]
@@ -136,7 +169,14 @@ private void AutoStartup()
136169
{
137170
try
138171
{
139-
Helper.AutoStartup.Enable();
172+
if (_settings.UseLogonTaskForStartup)
173+
{
174+
Helper.AutoStartup.EnableViaLogonTask();
175+
}
176+
else
177+
{
178+
Helper.AutoStartup.EnableViaRegistry();
179+
}
140180
}
141181
catch (Exception e)
142182
{

Flow.Launcher/CustomQueryHotkeySetting.xaml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,11 @@
3232
<StackPanel>
3333
<Grid>
3434
<Grid.ColumnDefinitions>
35-
<ColumnDefinition Width="Auto" />
3635
<ColumnDefinition Width="*" />
3736
<ColumnDefinition Width="Auto" />
38-
<ColumnDefinition Width="Auto" />
39-
<ColumnDefinition Width="Auto" />
4037
</Grid.ColumnDefinitions>
4138
<Button
42-
Grid.Column="4"
39+
Grid.Column="1"
4340
Click="BtnCancel_OnClick"
4441
Style="{StaticResource TitleBarCloseButtonStyle}">
4542
<Path

Flow.Launcher/CustomShortcutSetting.xaml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,11 @@
3030
<StackPanel>
3131
<Grid>
3232
<Grid.ColumnDefinitions>
33-
<ColumnDefinition Width="Auto" />
3433
<ColumnDefinition Width="*" />
3534
<ColumnDefinition Width="Auto" />
36-
<ColumnDefinition Width="Auto" />
37-
<ColumnDefinition Width="Auto" />
3835
</Grid.ColumnDefinitions>
3936
<Button
40-
Grid.Column="4"
37+
Grid.Column="1"
4138
Click="BtnCancel_OnClick"
4239
Style="{StaticResource TitleBarCloseButtonStyle}">
4340
<Path

Flow.Launcher/Flow.Launcher.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@
9090
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
9191
</PackageReference>
9292
<PackageReference Include="InputSimulator" Version="1.0.4" />
93-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
94-
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
93+
<!-- Do not upgrade Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting since we are .Net7.0 -->
94+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
95+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
9596
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
9697
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
9798
<PrivateAssets>all</PrivateAssets>
@@ -103,6 +104,7 @@
103104
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
104105
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
105106
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
107+
<PackageReference Include="Jack251970.TaskScheduler" Version="2.12.1" />
106108
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
107109
</ItemGroup>
108110

Flow.Launcher/Helper/AutoStartup.cs

Lines changed: 138 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,74 @@ 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+
return true;
61+
}
62+
catch (Exception e)
63+
{
64+
Log.Error("AutoStartup", $"Failed to check logon task: {e}");
65+
}
66+
}
67+
68+
return false;
69+
}
70+
71+
public static void DisableViaLogonTaskAndRegistry()
72+
{
73+
Disable(true);
74+
Disable(false);
75+
}
76+
77+
public static void EnableViaLogonTask()
78+
{
79+
Enable(true);
80+
}
81+
82+
public static void EnableViaRegistry()
83+
{
84+
Enable(false);
85+
}
86+
87+
public static void ChangeToViaLogonTask()
88+
{
89+
Disable(false);
90+
Enable(true);
91+
}
92+
93+
public static void ChangeToViaRegistry()
94+
{
95+
Disable(true);
96+
Enable(false);
97+
}
98+
99+
private static void Disable(bool logonTask)
32100
{
33101
try
34102
{
35-
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
36-
key?.DeleteValue(Constant.FlowLauncher, false);
103+
if (logonTask)
104+
{
105+
UnscheduleLogonTask();
106+
}
107+
else
108+
{
109+
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
110+
key?.DeleteValue(Constant.FlowLauncher, false);
111+
}
37112
}
38113
catch (Exception e)
39114
{
@@ -42,17 +117,74 @@ public static void Disable()
42117
}
43118
}
44119

45-
internal static void Enable()
120+
private static void Enable(bool logonTask)
46121
{
47122
try
48123
{
49-
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
50-
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
124+
if (logonTask)
125+
{
126+
ScheduleLogonTask();
127+
}
128+
else
129+
{
130+
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
131+
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
132+
}
51133
}
52134
catch (Exception e)
53135
{
54136
Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}");
55137
throw;
56138
}
57139
}
140+
141+
private static bool ScheduleLogonTask()
142+
{
143+
using var td = TaskService.Instance.NewTask();
144+
td.RegistrationInfo.Description = LogonTaskDesc;
145+
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
146+
td.Actions.Add(Constant.ExecutablePath);
147+
148+
if (IsCurrentUserIsAdmin())
149+
{
150+
td.Principal.RunLevel = TaskRunLevel.Highest;
151+
}
152+
153+
td.Settings.StopIfGoingOnBatteries = false;
154+
td.Settings.DisallowStartIfOnBatteries = false;
155+
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;
156+
157+
try
158+
{
159+
TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td);
160+
return true;
161+
}
162+
catch (Exception e)
163+
{
164+
Log.Error("AutoStartup", $"Failed to schedule logon task: {e}");
165+
return false;
166+
}
167+
}
168+
169+
private static bool UnscheduleLogonTask()
170+
{
171+
using var taskService = new TaskService();
172+
try
173+
{
174+
taskService.RootFolder.DeleteTask(LogonTaskName);
175+
return true;
176+
}
177+
catch (Exception e)
178+
{
179+
Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}");
180+
return false;
181+
}
182+
}
183+
184+
private static bool IsCurrentUserIsAdmin()
185+
{
186+
var identity = WindowsIdentity.GetCurrent();
187+
var principal = new WindowsPrincipal(identity);
188+
return principal.IsInRole(WindowsBuiltInRole.Administrator);
189+
}
58190
}

0 commit comments

Comments
 (0)