From 24327533c6e165f0cfa02e9a822fcf973c1880df Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 1 Apr 2025 14:16:32 +0800 Subject: [PATCH 01/20] Add plugin json storage --- .../Image/ImageLoader.cs | 1 + .../Storage/BinaryStorage.cs | 60 +++++++++++++++---- .../Storage/PluginBinaryStorage.cs | 15 +++++ .../Storage/PluginJsonStorage.cs | 6 -- 4 files changed, 64 insertions(+), 18 deletions(-) create mode 100644 Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 6f7b1cd908d..cca4bd2a4f1 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -37,6 +37,7 @@ public static async Task InitializeAsync() _hashGenerator = new ImageHashGenerator(); var usage = await LoadStorageToConcurrentDictionaryAsync(); + _storage.ClearData(); ImageCache.Initialize(usage); diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 5b73faae6a6..69ac6000b76 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -4,6 +4,8 @@ using Flow.Launcher.Infrastructure.UserSettings; using MemoryPack; +#nullable enable + namespace Flow.Launcher.Infrastructure.Storage { /// @@ -15,40 +17,53 @@ namespace Flow.Launcher.Infrastructure.Storage /// public class BinaryStorage { + protected T? Data; + public const string FileSuffix = ".cache"; + protected string FilePath { get; init; } = null!; + + protected string DirectoryPath { get; init; } = null!; + // Let the derived class to set the file path - public BinaryStorage(string filename, string directoryPath = null) + protected BinaryStorage() { - directoryPath ??= DataLocation.CacheDirectory; - Helper.ValidateDirectory(directoryPath); - - FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}"); } - public string FilePath { get; } + public BinaryStorage(string filename) + { + DirectoryPath = DataLocation.CacheDirectory; + Helper.ValidateDirectory(DirectoryPath); + + FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}"); + } public async ValueTask TryLoadAsync(T defaultData) { + if (Data != null) + return Data; + if (File.Exists(FilePath)) { if (new FileInfo(FilePath).Length == 0) { Log.Error($"|BinaryStorage.TryLoad|Zero length cache file <{FilePath}>"); - await SaveAsync(defaultData); - return defaultData; + Data = defaultData; + await SaveAsync(); } await using var stream = new FileStream(FilePath, FileMode.Open); var d = await DeserializeAsync(stream, defaultData); - return d; + Data = d; } else { Log.Info("|BinaryStorage.TryLoad|Cache file not exist, load default data"); - await SaveAsync(defaultData); - return defaultData; + Data = defaultData; + await SaveAsync(); } + + return Data; } private static async ValueTask DeserializeAsync(Stream stream, T defaultData) @@ -56,7 +71,7 @@ private static async ValueTask DeserializeAsync(Stream stream, T defaultData) try { var t = await MemoryPackSerializer.DeserializeAsync(stream); - return t; + return t ?? defaultData; } catch (System.Exception) { @@ -65,6 +80,27 @@ private static async ValueTask DeserializeAsync(Stream stream, T defaultData) } } + public async ValueTask SaveAsync() + { + await using var stream = new FileStream(FilePath, FileMode.Create); + await MemoryPackSerializer.SerializeAsync(stream, Data); + } + + // For SavePluginSettings function + public void Save() + { + var serialized = MemoryPackSerializer.Serialize(Data); + + File.WriteAllBytes(FilePath, serialized); + } + + // ImageCache need to be converted into concurrent dictionary, so it does not need to cache loading results into Data + public void ClearData() + { + Data = default; + } + + // ImageCache storages data in its class, so it needs to pass it to SaveAsync public async ValueTask SaveAsync(T data) { await using var stream = new FileStream(FilePath, FileMode.Create); diff --git a/Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs new file mode 100644 index 00000000000..87f51d5d773 --- /dev/null +++ b/Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Flow.Launcher.Infrastructure.Storage +{ + public class PluginBinaryStorage : BinaryStorage where T : new() + { + public PluginBinaryStorage(string cacheName, string cacheDirectory) + { + DirectoryPath = cacheDirectory; + Helper.ValidateDirectory(DirectoryPath); + + FilePath = Path.Combine(DirectoryPath, $"{cacheName}{FileSuffix}"); + } + } +} diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index b377c81aa14..9c6547c6684 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -10,7 +10,6 @@ namespace Flow.Launcher.Infrastructure.Storage public PluginJsonStorage() { - // C# related, add python related below var dataType = typeof(T); AssemblyName = dataType.Assembly.GetName().Name; DirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, AssemblyName); @@ -18,10 +17,5 @@ public PluginJsonStorage() FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); } - - public PluginJsonStorage(T data) : this() - { - Data = data; - } } } From ca221d710026b3e402873a174696a48e28e5d958 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 1 Apr 2025 14:19:59 +0800 Subject: [PATCH 02/20] Add binary storage api functions --- .../JsonRPCV2Models/JsonRPCPublicAPI.cs | 5 ++ Flow.Launcher.Core/Plugin/PluginManager.cs | 5 +- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 33 ++++++++++++ Flow.Launcher.Plugin/Interfaces/ISavable.cs | 7 +-- Flow.Launcher/PublicAPIInstance.cs | 53 ++++++++++++++++--- 5 files changed, 91 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs b/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs index 8df2ce9ed9e..cf1c57f3ece 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCV2Models/JsonRPCPublicAPI.cs @@ -185,5 +185,10 @@ public void StopLoadingBar() { _api.StopLoadingBar(); } + + public void SavePluginCaches() + { + _api.SavePluginCaches(); + } } } diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 4f869901ca8..fa4e43e0713 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -66,6 +66,7 @@ public static void Save() } API.SavePluginSettings(); + API.SavePluginCaches(); } public static async ValueTask DisposePluginsAsync() @@ -587,11 +588,13 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool remo if (removePluginSettings) { - // For dotnet plugins, we need to remove their PluginJsonStorage instance + // For dotnet plugins, we need to remove their PluginJsonStorage and PluginBinaryStorage instances if (AllowedLanguage.IsDotNet(plugin.Language)) { var method = API.GetType().GetMethod("RemovePluginSettings"); method?.Invoke(API, new object[] { plugin.AssemblyName }); + var method1 = API.GetType().GetMethod("RemovePluginCache"); + method1?.Invoke(API, new object[] { plugin.PluginCacheDirectoryPath }); } try diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index f178ebb90d4..be776b06868 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -344,5 +344,38 @@ public interface IPublicAPI /// Stop the loading bar in main window /// public void StopLoadingBar(); + + /// + /// Save all Flow's plugins caches + /// + void SavePluginCaches(); + + /// + /// Load BinaryStorage for current plugin's cache. This is the method used to load cache from binary in Flow. + /// When the file is not exist, it will create a new instance for the specific type. + /// + /// Type for deserialization + /// Cache file name + /// Cache directory from plugin metadata + /// Default data to return + /// + /// + /// BinaryStorage utilize MemoryPack, which means the object must be MemoryPackSerializable + /// + Task LoadCacheBinaryStorageAsync(string cacheName, string cacheDirectory, T defaultData) where T : new(); + + /// + /// Save BinaryStorage for current plugin's cache. This is the method used to save cache to binary in Flow.Launcher + /// This method will save the original instance loaded with LoadCacheBinaryStorageAsync. + /// This API call is for manually Save. Flow will automatically save all cache type that has called LoadCacheBinaryStorageAsync or SaveCacheBinaryStorageAsync previously. + /// + /// Type for Serialization + /// Cache file name + /// Cache directory from plugin metadata + /// + /// + /// BinaryStorage utilize MemoryPack, which means the object must be MemoryPackSerializable + /// + Task SaveCacheBinaryStorageAsync(string cacheName, string cacheDirectory) where T : new(); } } diff --git a/Flow.Launcher.Plugin/Interfaces/ISavable.cs b/Flow.Launcher.Plugin/Interfaces/ISavable.cs index 77bd304e4ea..cabd269624e 100644 --- a/Flow.Launcher.Plugin/Interfaces/ISavable.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISavable.cs @@ -1,11 +1,12 @@ -namespace Flow.Launcher.Plugin +namespace Flow.Launcher.Plugin { /// /// Inherit this interface if additional data e.g. cache needs to be saved. /// /// /// For storing plugin settings, prefer - /// or . + /// or . + /// or . /// Once called, your settings will be automatically saved by Flow. /// public interface ISavable : IFeatures @@ -15,4 +16,4 @@ public interface ISavable : IFeatures /// void Save(); } -} \ No newline at end of file +} diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index e19ad2fdcd4..17d7e103e9a 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -236,14 +236,6 @@ public void SavePluginSettings() ((PluginJsonStorage)_pluginJsonStorages[type]).Save(); } - public void SaveJsonStorage(T settings) where T : new() - { - var type = typeof(T); - _pluginJsonStorages[type] = new PluginJsonStorage(settings); - - ((PluginJsonStorage)_pluginJsonStorages[type]).Save(); - } - public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null) { using var explorer = new Process(); @@ -342,6 +334,51 @@ public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", M public Task ShowProgressBoxAsync(string caption, Func, Task> reportProgressAsync, Action cancelProgress = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, cancelProgress); + private readonly ConcurrentDictionary<(string, string, Type), object> _pluginBinaryStorages = new(); + + public void RemovePluginCache(string cacheDirectory) + { + foreach (var keyValuePair in _pluginBinaryStorages) + { + var key = keyValuePair.Key; + var currentCacheDirectory = key.Item2; + if (cacheDirectory == currentCacheDirectory) + { + _pluginBinaryStorages.Remove(key, out var _); + } + } + } + + /// + /// Save plugin caches. + /// + public void SavePluginCaches() + { + foreach (var value in _pluginBinaryStorages.Values) + { + var method = value.GetType().GetMethod("Save"); + method?.Invoke(value, null); + } + } + + public async Task LoadCacheBinaryStorageAsync(string cacheName, string cacheDirectory, T defaultData) where T : new() + { + var type = typeof(T); + if (!_pluginBinaryStorages.ContainsKey((cacheName, cacheDirectory, type))) + _pluginBinaryStorages[(cacheName, cacheDirectory, type)] = new PluginBinaryStorage(cacheName, cacheDirectory); + + return await ((PluginBinaryStorage)_pluginBinaryStorages[(cacheName, cacheDirectory, type)]).TryLoadAsync(defaultData); + } + + public async Task SaveCacheBinaryStorageAsync(string cacheName, string cacheDirectory) where T : new() + { + var type = typeof(T); + if (!_pluginBinaryStorages.ContainsKey((cacheName, cacheDirectory, type))) + _pluginBinaryStorages[(cacheName, cacheDirectory, type)] = new PluginBinaryStorage(cacheName, cacheDirectory); + + await ((PluginBinaryStorage)_pluginBinaryStorages[(cacheName, cacheDirectory, type)]).SaveAsync(); + } + #endregion #region Private Methods From 0496d6c04ac2bed323060f41519c41fe1358dc73 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 1 Apr 2025 14:21:29 +0800 Subject: [PATCH 03/20] Use api functions for Program plugin --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 82 +++++++++---------- .../Programs/UWPPackage.cs | 2 +- .../Programs/Win32.cs | 2 +- .../Views/ProgramSetting.xaml.cs | 6 +- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 3be23214c86..5bc518105cf 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -8,7 +8,6 @@ using System.Windows.Controls; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.Program.Programs; using Flow.Launcher.Plugin.Program.Views; @@ -19,19 +18,17 @@ namespace Flow.Launcher.Plugin.Program { - public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISavable, IAsyncReloadable, - IDisposable + public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, IAsyncReloadable, IDisposable { - internal static Win32[] _win32s { get; set; } - internal static UWPApp[] _uwps { get; set; } - internal static Settings _settings { get; set; } + private const string Win32CacheName = "Win32"; + private const string UwpCacheName = "UWP"; + internal static List _win32s { get; private set; } + internal static List _uwps { get; private set; } + internal static Settings _settings { get; private set; } internal static PluginInitContext Context { get; private set; } - private static BinaryStorage _win32Storage; - private static BinaryStorage _uwpStorage; - private static readonly List emptyResults = new(); private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 }; @@ -81,12 +78,6 @@ static Main() { } - public void Save() - { - _win32Storage.SaveAsync(_win32s); - _uwpStorage.SaveAsync(_uwps); - } - public async Task> QueryAsync(Query query, CancellationToken token) { var result = await cache.GetOrCreateAsync(query.Search, async entry => @@ -191,7 +182,9 @@ public async Task InitAsync(PluginInitContext context) await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", async () => { - Helper.ValidateDirectory(Context.CurrentPluginMetadata.PluginCacheDirectoryPath); + var pluginCachePath = Context.CurrentPluginMetadata.PluginCacheDirectoryPath; + + Helper.ValidateDirectory(pluginCachePath); static void MoveFile(string sourcePath, string destinationPath) { @@ -236,20 +229,18 @@ static void MoveFile(string sourcePath, string destinationPath) } // Move old cache files to the new cache directory - var oldWin32CacheFile = Path.Combine(DataLocation.CacheDirectory, $"Win32.cache"); - var newWin32CacheFile = Path.Combine(Context.CurrentPluginMetadata.PluginCacheDirectoryPath, $"Win32.cache"); + var oldWin32CacheFile = Path.Combine(DataLocation.CacheDirectory, $"{Win32CacheName}.cache"); + var newWin32CacheFile = Path.Combine(pluginCachePath, $"{Win32CacheName}.cache"); MoveFile(oldWin32CacheFile, newWin32CacheFile); - var oldUWPCacheFile = Path.Combine(DataLocation.CacheDirectory, $"UWP.cache"); - var newUWPCacheFile = Path.Combine(Context.CurrentPluginMetadata.PluginCacheDirectoryPath, $"UWP.cache"); + var oldUWPCacheFile = Path.Combine(DataLocation.CacheDirectory, $"{UwpCacheName}.cache"); + var newUWPCacheFile = Path.Combine(pluginCachePath, $"{UwpCacheName}.cache"); MoveFile(oldUWPCacheFile, newUWPCacheFile); - _win32Storage = new BinaryStorage("Win32", Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - _win32s = await _win32Storage.TryLoadAsync(Array.Empty()); - _uwpStorage = new BinaryStorage("UWP", Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - _uwps = await _uwpStorage.TryLoadAsync(Array.Empty()); + _win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCachePath, new List()); + _uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCachePath, new List()); }); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Count}>"); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Count}>"); bool cacheEmpty = !_win32s.Any() || !_uwps.Any(); @@ -273,36 +264,45 @@ static void WatchProgramUpdate() } } - public static void IndexWin32Programs() + public static async Task IndexWin32ProgramsAsync() { var win32S = Win32.All(_settings); - _win32s = win32S; + _win32s.Clear(); + foreach (var win32 in win32S) + { + _win32s.Add(win32); + } ResetCache(); - _win32Storage.SaveAsync(_win32s); + await Context.API.SaveCacheBinaryStorageAsync>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _settings.LastIndexTime = DateTime.Now; } - public static void IndexUwpPrograms() + public static async Task IndexUwpProgramsAsync() { - var applications = UWPPackage.All(_settings); - _uwps = applications; + var uwps = UWPPackage.All(_settings); + _uwps.Clear(); + foreach (var uwp in uwps) + { + _uwps.Add(uwp); + } ResetCache(); - _uwpStorage.SaveAsync(_uwps); + await Context.API.SaveCacheBinaryStorageAsync>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _settings.LastIndexTime = DateTime.Now; } public static async Task IndexProgramsAsync() { - var a = Task.Run(() => + var win32Task = Task.Run(async () => { - Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs); + await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32ProgramsAsync); }); - var b = Task.Run(() => + var uwpTask = Task.Run(async () => { - Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|UWPProgram index cost", IndexUwpPrograms); + await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|UWPProgram index cost", IndexUwpProgramsAsync); }); - await Task.WhenAll(a, b).ConfigureAwait(false); + + await Task.WhenAll(win32Task, uwpTask).ConfigureAwait(false); } internal static void ResetCache() @@ -314,7 +314,7 @@ internal static void ResetCache() public Control CreateSettingPanel() { - return new ProgramSetting(Context, _settings, _win32s, _uwps); + return new ProgramSetting(Context, _settings); } public string GetTranslatedPluginTitle() @@ -370,7 +370,7 @@ private static void DisableProgram(IProgram programToDelete) _settings.DisabledProgramSources.Add(new ProgramSource(program)); _ = Task.Run(() => { - IndexUwpPrograms(); + _ = IndexUwpProgramsAsync(); }); } else if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) @@ -380,7 +380,7 @@ private static void DisableProgram(IProgram programToDelete) _settings.DisabledProgramSources.Add(new ProgramSource(program)); _ = Task.Run(() => { - IndexWin32Programs(); + _ = IndexWin32ProgramsAsync(); }); } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs index 654897cc57f..bf100ed7ee3 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs @@ -317,7 +317,7 @@ public static async Task WatchPackageChange() { await Task.Delay(3000).ConfigureAwait(false); PackageChangeChannel.Reader.TryRead(out _); - await Task.Run(Main.IndexUwpPrograms); + await Task.Run(Main.IndexUwpProgramsAsync); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index a64a708efd3..06be2a628cf 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -797,7 +797,7 @@ public static async Task MonitorDirectoryChangeAsync() { } - await Task.Run(Main.IndexWin32Programs); + await Task.Run(Main.IndexWin32ProgramsAsync); } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs index 91864cb680c..5ad7fcea369 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs @@ -18,8 +18,8 @@ namespace Flow.Launcher.Plugin.Program.Views /// public partial class ProgramSetting : UserControl { - private PluginInitContext context; - private Settings _settings; + private readonly PluginInitContext context; + private readonly Settings _settings; private GridViewColumnHeader _lastHeaderClicked; private ListSortDirection _lastDirection; @@ -109,7 +109,7 @@ public bool EnableUWP public bool ShowUWPCheckbox => UWPPackage.SupportUWP(); - public ProgramSetting(PluginInitContext context, Settings settings, Win32[] win32s, UWPApp[] uwps) + public ProgramSetting(PluginInitContext context, Settings settings) { this.context = context; _settings = settings; From 68e1fc28efcc31362193aab4e73aa7459bf1b1af Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 1 Apr 2025 14:35:43 +0800 Subject: [PATCH 04/20] Use try remove for safety --- Flow.Launcher/PublicAPIInstance.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 17d7e103e9a..77019555082 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -201,7 +201,7 @@ public void RemovePluginSettings(string assemblyName) var name = value.GetType().GetField("AssemblyName")?.GetValue(value)?.ToString(); if (name == assemblyName) { - _pluginJsonStorages.Remove(key, out var pluginJsonStorage); + _pluginJsonStorages.TryRemove(key, out var pluginJsonStorage); } } } @@ -344,7 +344,7 @@ public void RemovePluginCache(string cacheDirectory) var currentCacheDirectory = key.Item2; if (cacheDirectory == currentCacheDirectory) { - _pluginBinaryStorages.Remove(key, out var _); + _pluginBinaryStorages.TryRemove(key, out var _); } } } From 56536d018891a7a28e1c5916a1426045b3682360 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 16:45:58 +0800 Subject: [PATCH 05/20] Add log for plugin binary storage --- .../Storage/PluginBinaryStorage.cs | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs index 87f51d5d773..d18060e3df0 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs @@ -1,15 +1,49 @@ using System.IO; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Plugin; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Infrastructure.Storage { public class PluginBinaryStorage : BinaryStorage where T : new() { + private static readonly string ClassName = "PluginBinaryStorage"; + + // We should not initialize API in static constructor because it will create another API instance + private static IPublicAPI api = null; + private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); + public PluginBinaryStorage(string cacheName, string cacheDirectory) { DirectoryPath = cacheDirectory; - Helper.ValidateDirectory(DirectoryPath); + FilesFolders.ValidateDirectory(DirectoryPath); FilePath = Path.Combine(DirectoryPath, $"{cacheName}{FileSuffix}"); } + + public new void Save() + { + try + { + base.Save(); + } + catch (System.Exception e) + { + API.LogException(ClassName, $"Failed to save plugin caches to path: {FilePath}", e); + } + } + + public new async Task SaveAsync() + { + try + { + await base.SaveAsync(); + } + catch (System.Exception e) + { + API.LogException(ClassName, $"Failed to save plugin caches to path: {FilePath}", e); + } + } } } From e6d3d0fa9efba36feec5dbeb68e5e41e3af47039 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 5 Apr 2025 11:11:14 +0800 Subject: [PATCH 06/20] Improve code quality --- .../Storage/BinaryStorage.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 81e2503adc4..208e4bf119b 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -81,13 +81,6 @@ private static async ValueTask DeserializeAsync(Stream stream, T defaultData) } } - public async ValueTask SaveAsync() - { - await using var stream = new FileStream(FilePath, FileMode.Create); - await MemoryPackSerializer.SerializeAsync(stream, Data); - } - - // For SavePluginSettings function public void Save() { var serialized = MemoryPackSerializer.Serialize(Data); @@ -95,13 +88,20 @@ public void Save() File.WriteAllBytes(FilePath, serialized); } - // ImageCache need to be converted into concurrent dictionary, so it does not need to cache loading results into Data + public async ValueTask SaveAsync() + { + await SaveAsync(Data.NonNull()); + } + + // ImageCache need to convert data into concurrent dictionary for usage, + // so we would better to clear the data public void ClearData() { Data = default; } - // ImageCache storages data in its class, so it needs to pass it to SaveAsync + // ImageCache storages data in its class, + // so we need to pass it to SaveAsync public async ValueTask SaveAsync(T data) { await using var stream = new FileStream(FilePath, FileMode.Create); From 6e5c7ad190a539903f068995bac117b15ca45cc3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 5 Apr 2025 11:19:01 +0800 Subject: [PATCH 07/20] Use ISavable interface instead of reflection --- Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs | 3 ++- Flow.Launcher.Infrastructure/Storage/JsonStorage.cs | 8 +++++--- Flow.Launcher/PublicAPIInstance.cs | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 208e4bf119b..f218c5d8d0d 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; using MemoryPack; @@ -16,7 +17,7 @@ namespace Flow.Launcher.Infrastructure.Storage /// /// It utilize MemoryPack, which means the object must be MemoryPackSerializable /// - public class BinaryStorage + public class BinaryStorage : ISavable { protected T? Data; diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index a3488124b92..cdf3ae90962 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -1,18 +1,20 @@ -#nullable enable -using System; +using System; using System.Globalization; using System.IO; using System.Text.Json; using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; +#nullable enable + namespace Flow.Launcher.Infrastructure.Storage { /// /// Serialize object using json format. /// - public class JsonStorage where T : new() + public class JsonStorage : ISavable where T : new() { protected T? Data; diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 99d4cf2332a..50052f1c9c3 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -225,8 +225,8 @@ public void SavePluginSettings() { foreach (var value in _pluginJsonStorages.Values) { - var method = value.GetType().GetMethod("Save"); - method?.Invoke(value, null); + var savable = value as ISavable; + savable?.Save(); } } @@ -368,8 +368,8 @@ public void SavePluginCaches() { foreach (var value in _pluginBinaryStorages.Values) { - var method = value.GetType().GetMethod("Save"); - method?.Invoke(value, null); + var savable = value as ISavable; + savable?.Save(); } } From 3185bda824ab19a132703d77dbf7c6844dd0a2a8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 5 Apr 2025 11:31:24 +0800 Subject: [PATCH 08/20] Use IRemovable interface instead of reflection --- Flow.Launcher.Core/Plugin/PluginManager.cs | 9 ++++----- Flow.Launcher.Core/Storage/IRemovable.cs | 19 +++++++++++++++++++ Flow.Launcher/PublicAPIInstance.cs | 7 ++++--- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 Flow.Launcher.Core/Storage/IRemovable.cs diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index c167a5b2184..03ddcb13d4e 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -13,6 +13,7 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; +using IRemovable = Flow.Launcher.Core.Storage.IRemovable; using ISavable = Flow.Launcher.Plugin.ISavable; namespace Flow.Launcher.Core.Plugin @@ -588,12 +589,10 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool remo if (removePluginSettings) { // For dotnet plugins, we need to remove their PluginJsonStorage and PluginBinaryStorage instances - if (AllowedLanguage.IsDotNet(plugin.Language)) + if (AllowedLanguage.IsDotNet(plugin.Language) && API is IRemovable removable) { - var method = API.GetType().GetMethod("RemovePluginSettings"); - method?.Invoke(API, new object[] { plugin.AssemblyName }); - var method1 = API.GetType().GetMethod("RemovePluginCache"); - method1?.Invoke(API, new object[] { plugin.PluginCacheDirectoryPath }); + removable.RemovePluginSettings(plugin.AssemblyName); + removable.RemovePluginCaches(plugin.PluginCacheDirectoryPath); } try diff --git a/Flow.Launcher.Core/Storage/IRemovable.cs b/Flow.Launcher.Core/Storage/IRemovable.cs new file mode 100644 index 00000000000..fc34395e062 --- /dev/null +++ b/Flow.Launcher.Core/Storage/IRemovable.cs @@ -0,0 +1,19 @@ +namespace Flow.Launcher.Core.Storage; + +/// +/// Remove storage instances from instance +/// +public interface IRemovable +{ + /// + /// Remove all instances of one plugin + /// + /// + public void RemovePluginSettings(string assemblyName); + + /// + /// Remove all instances of one plugin + /// + /// + public void RemovePluginCaches(string cacheDirectory); +} diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 50052f1c9c3..b3400627283 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -14,6 +14,8 @@ using Squirrel; using Flow.Launcher.Core; using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Core.Resource; +using Flow.Launcher.Core.Storage; using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Http; @@ -27,11 +29,10 @@ using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.ViewModel; using JetBrains.Annotations; -using Flow.Launcher.Core.Resource; namespace Flow.Launcher { - public class PublicAPIInstance : IPublicAPI + public class PublicAPIInstance : IPublicAPI, IRemovable { private readonly Settings _settings; private readonly Internationalization _translater; @@ -348,7 +349,7 @@ public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", M private readonly ConcurrentDictionary<(string, string, Type), object> _pluginBinaryStorages = new(); - public void RemovePluginCache(string cacheDirectory) + public void RemovePluginCaches(string cacheDirectory) { foreach (var keyValuePair in _pluginBinaryStorages) { From 7da2884e84ebd45dc70c16cd3dde6f6ef1e2b4af Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 16:29:03 +0800 Subject: [PATCH 09/20] Add locks for win32s & uwps --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 61 +++++++++++++++---- .../Views/Commands/ProgramSettingDisplay.cs | 34 ++++++++--- .../Views/ProgramSetting.xaml.cs | 18 +++--- 3 files changed, 83 insertions(+), 30 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index d3c50b40649..6d2ae70fcdd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -27,6 +27,9 @@ public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, I internal static List _uwps { get; private set; } internal static Settings _settings { get; private set; } + internal static SemaphoreSlim _win32sLock = new(1, 1); + internal static SemaphoreSlim _uwpsLock = new(1, 1); + internal static PluginInitContext Context { get; private set; } private static readonly List emptyResults = new(); @@ -82,8 +85,11 @@ public async Task> QueryAsync(Query query, CancellationToken token) { var result = await cache.GetOrCreateAsync(query.Search, async entry => { - var resultList = await Task.Run(() => + var resultList = await Task.Run(async () => { + await _win32sLock.WaitAsync(token); + await _uwpsLock.WaitAsync(token); + try { // Collect all UWP Windows app directories @@ -95,22 +101,26 @@ public async Task> QueryAsync(Query query, CancellationToken token) .ToArray() : null; return _win32s.Cast() - .Concat(_uwps) - .AsParallel() - .WithCancellation(token) - .Where(HideUninstallersFilter) - .Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories)) - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, Context.API)) - .Where(r => r?.Score > 0) - .ToList(); + .Concat(_uwps) + .AsParallel() + .WithCancellation(token) + .Where(HideUninstallersFilter) + .Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories)) + .Where(p => p.Enabled) + .Select(p => p.Result(query.Search, Context.API)) + .Where(r => r?.Score > 0) + .ToList(); } catch (OperationCanceledException) { Log.Debug("|Flow.Launcher.Plugin.Program.Main|Query operation cancelled"); return emptyResults; } - + finally + { + _uwpsLock.Release(); + _win32sLock.Release(); + } }, token); resultList = resultList.Any() ? resultList : emptyResults; @@ -236,14 +246,25 @@ static void MoveFile(string sourcePath, string destinationPath) var newUWPCacheFile = Path.Combine(pluginCachePath, $"{UwpCacheName}.cache"); MoveFile(oldUWPCacheFile, newUWPCacheFile); + await _win32sLock.WaitAsync(); _win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCachePath, new List()); + _win32sLock.Release(); + + await _uwpsLock.WaitAsync(); _uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCachePath, new List()); + _uwpsLock.Release(); }); + await _win32sLock.WaitAsync(); + await _uwpsLock.WaitAsync(); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Count}>"); Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Count}>"); bool cacheEmpty = !_win32s.Any() || !_uwps.Any(); + _win32sLock.Release(); + _uwpsLock.Release(); + if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now) { _ = Task.Run(async () => @@ -267,11 +288,13 @@ static void WatchProgramUpdate() public static async Task IndexWin32ProgramsAsync() { var win32S = Win32.All(_settings); + await _win32sLock.WaitAsync(); _win32s.Clear(); foreach (var win32 in win32S) { _win32s.Add(win32); } + _win32sLock.Release(); ResetCache(); await Context.API.SaveCacheBinaryStorageAsync>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _settings.LastIndexTime = DateTime.Now; @@ -280,11 +303,13 @@ public static async Task IndexWin32ProgramsAsync() public static async Task IndexUwpProgramsAsync() { var uwps = UWPPackage.All(_settings); + await _uwpsLock.WaitAsync(); _uwps.Clear(); foreach (var uwp in uwps) { _uwps.Add(uwp); } + _uwpsLock.Release(); ResetCache(); await Context.API.SaveCacheBinaryStorageAsync>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _settings.LastIndexTime = DateTime.Now; @@ -358,26 +383,36 @@ public List LoadContextMenus(Result selectedResult) return menuOptions; } - private static void DisableProgram(IProgram programToDelete) + private static async Task DisableProgram(IProgram programToDelete) { if (_settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) return; + await _uwpsLock.WaitAsync(); if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) { var program = _uwps.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier); program.Enabled = false; _settings.DisabledProgramSources.Add(new ProgramSource(program)); + _uwpsLock.Release(); + + // Reindex UWP programs _ = Task.Run(() => { _ = IndexUwpProgramsAsync(); }); + return; } - else if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) + + await _win32sLock.WaitAsync(); + if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) { var program = _win32s.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier); program.Enabled = false; _settings.DisabledProgramSources.Add(new ProgramSource(program)); + _win32sLock.Release(); + + // Reindex Win32 programs _ = Task.Run(() => { _ = IndexWin32ProgramsAsync(); diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs index e4d7c323a5f..b89a2a6ba9e 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Flow.Launcher.Plugin.Program.Views.Models; namespace Flow.Launcher.Plugin.Program.Views.Commands @@ -15,21 +16,24 @@ internal static List LoadProgramSources() .ToList(); } - internal static void DisplayAllPrograms() + internal static async Task DisplayAllProgramsAsync() { + await Main._win32sLock.WaitAsync(); var win32 = Main._win32s .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)) .Select(x => new ProgramSource(x)); + ProgramSetting.ProgramSettingDisplayList.AddRange(win32); + Main._win32sLock.Release(); + await Main._uwpsLock.WaitAsync(); var uwp = Main._uwps .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)) .Select(x => new ProgramSource(x)); - - ProgramSetting.ProgramSettingDisplayList.AddRange(win32); ProgramSetting.ProgramSettingDisplayList.AddRange(uwp); + Main._uwpsLock.Release(); } - internal static void SetProgramSourcesStatus(List selectedProgramSourcesToDisable, bool status) + internal static async Task SetProgramSourcesStatusAsync(List selectedProgramSourcesToDisable, bool status) { foreach(var program in ProgramSetting.ProgramSettingDisplayList) { @@ -39,14 +43,17 @@ internal static void SetProgramSourcesStatus(List selectedProgram } } - foreach(var program in Main._win32s) + await Main._win32sLock.WaitAsync(); + foreach (var program in Main._win32s) { if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status)) { program.Enabled = status; } } + Main._win32sLock.Release(); + await Main._uwpsLock.WaitAsync(); foreach (var program in Main._uwps) { if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status)) @@ -54,6 +61,7 @@ internal static void SetProgramSourcesStatus(List selectedProgram program.Enabled = status; } } + Main._uwpsLock.Release(); } internal static void StoreDisabledInSettings() @@ -72,12 +80,22 @@ internal static void RemoveDisabledFromSettings() Main._settings.DisabledProgramSources.RemoveAll(t1 => t1.Enabled); } - internal static bool IsReindexRequired(this List selectedItems) + internal static async Task IsReindexRequiredAsync(this List selectedItems) { // Not in cache - if (selectedItems.Any(t1 => t1.Enabled && !Main._uwps.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)) + await Main._win32sLock.WaitAsync(); + await Main._uwpsLock.WaitAsync(); + try + { + if (selectedItems.Any(t1 => t1.Enabled && !Main._uwps.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)) && selectedItems.Any(t1 => t1.Enabled && !Main._win32s.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))) - return true; + return true; + } + finally + { + Main._win32sLock.Release(); + Main._uwpsLock.Release(); + } // ProgramSources holds list of user added directories, // so when we enable/disable we need to reindex to show/not show the programs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs index 5ad7fcea369..c42bd4f305d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs @@ -183,7 +183,7 @@ private void btnEditProgramSource_OnClick(object sender, RoutedEventArgs e) EditProgramSource(selectedProgramSource); } - private void EditProgramSource(ProgramSource selectedProgramSource) + private async void EditProgramSource(ProgramSource selectedProgramSource) { if (selectedProgramSource == null) { @@ -202,13 +202,13 @@ private void EditProgramSource(ProgramSource selectedProgramSource) { if (selectedProgramSource.Enabled) { - ProgramSettingDisplay.SetProgramSourcesStatus(new List { selectedProgramSource }, + await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List { selectedProgramSource }, true); // sync status in win32, uwp and disabled ProgramSettingDisplay.RemoveDisabledFromSettings(); } else { - ProgramSettingDisplay.SetProgramSourcesStatus(new List { selectedProgramSource }, + await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List { selectedProgramSource }, false); ProgramSettingDisplay.StoreDisabledInSettings(); } @@ -277,14 +277,14 @@ private void programSourceView_Drop(object sender, DragEventArgs e) } } - private void btnLoadAllProgramSource_OnClick(object sender, RoutedEventArgs e) + private async void btnLoadAllProgramSource_OnClick(object sender, RoutedEventArgs e) { - ProgramSettingDisplay.DisplayAllPrograms(); + await ProgramSettingDisplay.DisplayAllProgramsAsync(); ViewRefresh(); } - private void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e) + private async void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e) { var selectedItems = programSourceView .SelectedItems.Cast() @@ -311,18 +311,18 @@ private void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e) } else if (HasMoreOrEqualEnabledItems(selectedItems)) { - ProgramSettingDisplay.SetProgramSourcesStatus(selectedItems, false); + await ProgramSettingDisplay.SetProgramSourcesStatusAsync(selectedItems, false); ProgramSettingDisplay.StoreDisabledInSettings(); } else { - ProgramSettingDisplay.SetProgramSourcesStatus(selectedItems, true); + await ProgramSettingDisplay.SetProgramSourcesStatusAsync(selectedItems, true); ProgramSettingDisplay.RemoveDisabledFromSettings(); } - if (selectedItems.IsReindexRequired()) + if (await selectedItems.IsReindexRequiredAsync()) ReIndexing(); programSourceView.SelectedItems.Clear(); From 734c5bb67deb769190fa46572083e64ffcef8cf8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 16:59:27 +0800 Subject: [PATCH 10/20] Fix lock release issue --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 6d2ae70fcdd..a5fbc6c70cb 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -367,7 +367,7 @@ public List LoadContextMenus(Result selectedResult) Title = Context.API.GetTranslation("flowlauncher_plugin_program_disable_program"), Action = c => { - DisableProgram(program); + _ = DisableProgramAsync(program); Context.API.ShowMsg( Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"), Context.API.GetTranslation( @@ -383,7 +383,7 @@ public List LoadContextMenus(Result selectedResult) return menuOptions; } - private static async Task DisableProgram(IProgram programToDelete) + private static async Task DisableProgramAsync(IProgram programToDelete) { if (_settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) return; @@ -403,7 +403,12 @@ private static async Task DisableProgram(IProgram programToDelete) }); return; } - + else + { + // Release the lock if we cannot find the program + _uwpsLock.Release(); + } + await _win32sLock.WaitAsync(); if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) { @@ -418,6 +423,11 @@ private static async Task DisableProgram(IProgram programToDelete) _ = IndexWin32ProgramsAsync(); }); } + else + { + // Release the lock if we cannot find the program + _win32sLock.Release(); + } } public static void StartProcess(Func runProcess, ProcessStartInfo info) From c11ee2f9e78fa626bd7801fed293d990fdfaf682 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 17:20:57 +0800 Subject: [PATCH 11/20] Improve code quality & comments & Fix lock issue --- .../Storage/BinaryStorage.cs | 6 ++-- .../Storage/PluginJsonStorage.cs | 1 + Flow.Launcher.Plugin/Interfaces/ISavable.cs | 9 ++++-- Flow.Launcher/PublicAPIInstance.cs | 6 ---- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 29 +++++++------------ 5 files changed, 20 insertions(+), 31 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index f218c5d8d0d..b5de3b50ff1 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -42,8 +42,7 @@ public BinaryStorage(string filename) public async ValueTask TryLoadAsync(T defaultData) { - if (Data != null) - return Data; + if (Data != null) return Data; if (File.Exists(FilePath)) { @@ -55,8 +54,7 @@ public async ValueTask TryLoadAsync(T defaultData) } await using var stream = new FileStream(FilePath, FileMode.Open); - var d = await DeserializeAsync(stream, defaultData); - Data = d; + Data = await DeserializeAsync(stream, defaultData); } else { diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index b63e8c1ef70..e8cbd70fb98 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -20,6 +20,7 @@ namespace Flow.Launcher.Infrastructure.Storage public PluginJsonStorage() { + // C# related, add python related below var dataType = typeof(T); AssemblyName = dataType.Assembly.GetName().Name; DirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, AssemblyName); diff --git a/Flow.Launcher.Plugin/Interfaces/ISavable.cs b/Flow.Launcher.Plugin/Interfaces/ISavable.cs index cabd269624e..0d23c0fc8cd 100644 --- a/Flow.Launcher.Plugin/Interfaces/ISavable.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISavable.cs @@ -1,18 +1,21 @@ namespace Flow.Launcher.Plugin { /// - /// Inherit this interface if additional data e.g. cache needs to be saved. + /// Inherit this interface if additional data. + /// If you need to save data which is not a setting or cache, + /// please implement this interface. /// /// /// For storing plugin settings, prefer /// or . + /// For storing plugin caches, prefer /// or . - /// Once called, your settings will be automatically saved by Flow. + /// Once called, those settings and caches will be automatically saved by Flow. /// public interface ISavable : IFeatures { /// - /// Save additional plugin data, such as cache. + /// Save additional plugin data. /// void Save(); } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 4dd0268d25c..a523f90bb8f 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -229,9 +229,6 @@ public void RemovePluginSettings(string assemblyName) } } - /// - /// Save plugin settings. - /// public void SavePluginSettings() { foreach (var value in _pluginJsonStorages.Values) @@ -378,9 +375,6 @@ public void RemovePluginCaches(string cacheDirectory) } } - /// - /// Save plugin caches. - /// public void SavePluginCaches() { foreach (var value in _pluginBinaryStorages.Values) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index a5fbc6c70cb..561044981f5 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -77,10 +77,6 @@ public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, I private static readonly string WindowsAppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsApps"); - static Main() - { - } - public async Task> QueryAsync(Query query, CancellationToken token) { var result = await cache.GetOrCreateAsync(query.Search, async entry => @@ -101,15 +97,15 @@ public async Task> QueryAsync(Query query, CancellationToken token) .ToArray() : null; return _win32s.Cast() - .Concat(_uwps) - .AsParallel() - .WithCancellation(token) - .Where(HideUninstallersFilter) - .Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories)) - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, Context.API)) - .Where(r => r?.Score > 0) - .ToList(); + .Concat(_uwps) + .AsParallel() + .WithCancellation(token) + .Where(HideUninstallersFilter) + .Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories)) + .Where(p => p.Enabled) + .Select(p => p.Result(query.Search, Context.API)) + .Where(r => r?.Score > 0) + .ToList(); } catch (OperationCanceledException) { @@ -193,7 +189,6 @@ public async Task InitAsync(PluginInitContext context) await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", async () => { var pluginCachePath = Context.CurrentPluginMetadata.PluginCacheDirectoryPath; - FilesFolders.ValidateDirectory(pluginCachePath); static void MoveFile(string sourcePath, string destinationPath) @@ -294,10 +289,10 @@ public static async Task IndexWin32ProgramsAsync() { _win32s.Add(win32); } - _win32sLock.Release(); ResetCache(); await Context.API.SaveCacheBinaryStorageAsync>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _settings.LastIndexTime = DateTime.Now; + _win32sLock.Release(); } public static async Task IndexUwpProgramsAsync() @@ -309,10 +304,10 @@ public static async Task IndexUwpProgramsAsync() { _uwps.Add(uwp); } - _uwpsLock.Release(); ResetCache(); await Context.API.SaveCacheBinaryStorageAsync>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _settings.LastIndexTime = DateTime.Now; + _uwpsLock.Release(); } public static async Task IndexProgramsAsync() @@ -405,7 +400,6 @@ private static async Task DisableProgramAsync(IProgram programToDelete) } else { - // Release the lock if we cannot find the program _uwpsLock.Release(); } @@ -425,7 +419,6 @@ private static async Task DisableProgramAsync(IProgram programToDelete) } else { - // Release the lock if we cannot find the program _win32sLock.Release(); } } From 54e7652084245900e06c4513a1c6926ae13cf3b3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 17:22:32 +0800 Subject: [PATCH 12/20] Fix see cref issue --- Flow.Launcher.Core/Storage/IRemovable.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/Storage/IRemovable.cs b/Flow.Launcher.Core/Storage/IRemovable.cs index fc34395e062..bcf1cdd5e48 100644 --- a/Flow.Launcher.Core/Storage/IRemovable.cs +++ b/Flow.Launcher.Core/Storage/IRemovable.cs @@ -1,18 +1,18 @@ namespace Flow.Launcher.Core.Storage; /// -/// Remove storage instances from instance +/// Remove storage instances from instance /// public interface IRemovable { /// - /// Remove all instances of one plugin + /// Remove all instances of one plugin /// /// public void RemovePluginSettings(string assemblyName); /// - /// Remove all instances of one plugin + /// Remove all instances of one plugin /// /// public void RemovePluginCaches(string cacheDirectory); From 68268026de3261ea5eb7a28ff09f943adc816d0c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 19:21:03 +0800 Subject: [PATCH 13/20] Improve performance --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 561044981f5..c235fb5875f 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -186,6 +186,8 @@ public async Task InitAsync(PluginInitContext context) _settings = context.API.LoadSettingJsonStorage(); + var _win32sCount = 0; + var _uwpsCount = 0; await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", async () => { var pluginCachePath = Context.CurrentPluginMetadata.PluginCacheDirectoryPath; @@ -243,19 +245,18 @@ static void MoveFile(string sourcePath, string destinationPath) await _win32sLock.WaitAsync(); _win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCachePath, new List()); + _win32sCount = _win32s.Count; _win32sLock.Release(); await _uwpsLock.WaitAsync(); _uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCachePath, new List()); + _uwpsCount = _uwps.Count; _uwpsLock.Release(); }); - await _win32sLock.WaitAsync(); - await _uwpsLock.WaitAsync(); - - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Count}>"); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Count}>"); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32sCount}>"); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwpsCount}>"); - bool cacheEmpty = !_win32s.Any() || !_uwps.Any(); + var cacheEmpty = _win32sCount == 0 || _uwpsCount == 0; _win32sLock.Release(); _uwpsLock.Release(); From 4c4a6c0e22f65a7c1fd0865c70ad69b86e8a0aea Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 19:27:53 +0800 Subject: [PATCH 14/20] Add log handler for indexing --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 52 ++++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index c235fb5875f..11deb710d5f 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -283,32 +283,52 @@ static void WatchProgramUpdate() public static async Task IndexWin32ProgramsAsync() { - var win32S = Win32.All(_settings); await _win32sLock.WaitAsync(); - _win32s.Clear(); - foreach (var win32 in win32S) + try { - _win32s.Add(win32); + var win32S = Win32.All(_settings); + _win32s.Clear(); + foreach (var win32 in win32S) + { + _win32s.Add(win32); + } + ResetCache(); + await Context.API.SaveCacheBinaryStorageAsync>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); + _settings.LastIndexTime = DateTime.Now; + } + catch (Exception e) + { + Log.Exception("|Flow.Launcher.Plugin.Program.Main|Failed to index Win32 programs", e); + } + finally + { + _win32sLock.Release(); } - ResetCache(); - await Context.API.SaveCacheBinaryStorageAsync>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - _settings.LastIndexTime = DateTime.Now; - _win32sLock.Release(); } public static async Task IndexUwpProgramsAsync() { - var uwps = UWPPackage.All(_settings); await _uwpsLock.WaitAsync(); - _uwps.Clear(); - foreach (var uwp in uwps) + try { - _uwps.Add(uwp); + var uwps = UWPPackage.All(_settings); + _uwps.Clear(); + foreach (var uwp in uwps) + { + _uwps.Add(uwp); + } + ResetCache(); + await Context.API.SaveCacheBinaryStorageAsync>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); + _settings.LastIndexTime = DateTime.Now; + } + catch (Exception e) + { + Log.Exception("|Flow.Launcher.Plugin.Program.Main|Failed to index Uwp programs", e); + } + finally + { + _uwpsLock.Release(); } - ResetCache(); - await Context.API.SaveCacheBinaryStorageAsync>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - _settings.LastIndexTime = DateTime.Now; - _uwpsLock.Release(); } public static async Task IndexProgramsAsync() From aaadf167777a9374f07a523a11b3c102c403ee5b Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Tue, 8 Apr 2025 19:28:29 +0800 Subject: [PATCH 15/20] Fix typos Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 2f8672e137c..85c48f8a4ce 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -366,7 +366,7 @@ public interface IPublicAPI /// Default data to return /// /// - /// BinaryStorage utilize MemoryPack, which means the object must be MemoryPackSerializable + /// BinaryStorage utilizes MemoryPack, which means the object must be MemoryPackSerializable /// Task LoadCacheBinaryStorageAsync(string cacheName, string cacheDirectory, T defaultData) where T : new(); From 4749ca208abe9dbcfeb4f80470e2e6e489300e12 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 19:33:48 +0800 Subject: [PATCH 16/20] Improve code comments --- Flow.Launcher.Plugin/Interfaces/ISavable.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/Interfaces/ISavable.cs b/Flow.Launcher.Plugin/Interfaces/ISavable.cs index 0d23c0fc8cd..38cbf8e08f5 100644 --- a/Flow.Launcher.Plugin/Interfaces/ISavable.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISavable.cs @@ -1,8 +1,7 @@ namespace Flow.Launcher.Plugin { /// - /// Inherit this interface if additional data. - /// If you need to save data which is not a setting or cache, + /// Inherit this interface if you need to save additional data which is not a setting or cache, /// please implement this interface. /// /// From 2ff09cf9b0acb61e3323c80481e6eac5e2873891 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 19:41:02 +0800 Subject: [PATCH 17/20] Improve code quality --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 11deb710d5f..107673f8943 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -413,10 +413,7 @@ private static async Task DisableProgramAsync(IProgram programToDelete) _uwpsLock.Release(); // Reindex UWP programs - _ = Task.Run(() => - { - _ = IndexUwpProgramsAsync(); - }); + _ = Task.Run(IndexUwpProgramsAsync); return; } else @@ -433,10 +430,8 @@ private static async Task DisableProgramAsync(IProgram programToDelete) _win32sLock.Release(); // Reindex Win32 programs - _ = Task.Run(() => - { - _ = IndexWin32ProgramsAsync(); - }); + _ = Task.Run(IndexWin32ProgramsAsync); + return; } else { From 653b8335700290f175b7f9bce369a849d0d48f81 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 19:42:59 +0800 Subject: [PATCH 18/20] Fix typos --- Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs | 2 +- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index b5de3b50ff1..43bb8dadecb 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -15,7 +15,7 @@ namespace Flow.Launcher.Infrastructure.Storage /// Normally, it has better performance, but not readable /// /// - /// It utilize MemoryPack, which means the object must be MemoryPackSerializable + /// It utilizes MemoryPack, which means the object must be MemoryPackSerializable /// public class BinaryStorage : ISavable { diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 85c48f8a4ce..a3020b60725 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -380,7 +380,7 @@ public interface IPublicAPI /// Cache directory from plugin metadata /// /// - /// BinaryStorage utilize MemoryPack, which means the object must be MemoryPackSerializable + /// BinaryStorage utilizes MemoryPack, which means the object must be MemoryPackSerializable /// Task SaveCacheBinaryStorageAsync(string cacheName, string cacheDirectory) where T : new(); From d7ca36e60a39892295fe2c42c9e2f81e56b59a66 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 19:44:30 +0800 Subject: [PATCH 19/20] Change variable name --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 107673f8943..745b042e6a1 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -190,8 +190,8 @@ public async Task InitAsync(PluginInitContext context) var _uwpsCount = 0; await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", async () => { - var pluginCachePath = Context.CurrentPluginMetadata.PluginCacheDirectoryPath; - FilesFolders.ValidateDirectory(pluginCachePath); + var pluginCacheDirectory = Context.CurrentPluginMetadata.PluginCacheDirectoryPath; + FilesFolders.ValidateDirectory(pluginCacheDirectory); static void MoveFile(string sourcePath, string destinationPath) { @@ -237,19 +237,19 @@ static void MoveFile(string sourcePath, string destinationPath) // Move old cache files to the new cache directory var oldWin32CacheFile = Path.Combine(DataLocation.CacheDirectory, $"{Win32CacheName}.cache"); - var newWin32CacheFile = Path.Combine(pluginCachePath, $"{Win32CacheName}.cache"); + var newWin32CacheFile = Path.Combine(pluginCacheDirectory, $"{Win32CacheName}.cache"); MoveFile(oldWin32CacheFile, newWin32CacheFile); var oldUWPCacheFile = Path.Combine(DataLocation.CacheDirectory, $"{UwpCacheName}.cache"); - var newUWPCacheFile = Path.Combine(pluginCachePath, $"{UwpCacheName}.cache"); + var newUWPCacheFile = Path.Combine(pluginCacheDirectory, $"{UwpCacheName}.cache"); MoveFile(oldUWPCacheFile, newUWPCacheFile); await _win32sLock.WaitAsync(); - _win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCachePath, new List()); + _win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCacheDirectory, new List()); _win32sCount = _win32s.Count; _win32sLock.Release(); await _uwpsLock.WaitAsync(); - _uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCachePath, new List()); + _uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCacheDirectory, new List()); _uwpsCount = _uwps.Count; _uwpsLock.Release(); }); From 482e37316a1fee76b924e3b151a4507a42ad2cb7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 19:46:40 +0800 Subject: [PATCH 20/20] Remove useless releases --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 745b042e6a1..a50868b69dd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -258,9 +258,6 @@ static void MoveFile(string sourcePath, string destinationPath) var cacheEmpty = _win32sCount == 0 || _uwpsCount == 0; - _win32sLock.Release(); - _uwpsLock.Release(); - if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now) { _ = Task.Run(async () =>