diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..4596aa084 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,15 @@ + + + + + false + + + false + + + + \ No newline at end of file diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 87c390d34..9f146a457 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -54,12 +54,8 @@ - - - - diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs new file mode 100644 index 000000000..b9b878a7b --- /dev/null +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -0,0 +1,57 @@ +using Flow.Launcher.Infrastructure; +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace Flow.Launcher.Core.Plugin +{ + internal class PluginAssemblyLoader : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver dependencyResolver; + + private readonly AssemblyDependencyResolver referencedPluginPackageDependencyResolver; + + private readonly AssemblyName assemblyName; + + internal PluginAssemblyLoader(string assemblyFilePath) + { + dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath); + assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath)); + + referencedPluginPackageDependencyResolver = + new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.Plugin.dll")); + } + + internal Assembly LoadAssemblyAndDependencies() + { + return LoadFromAssemblyName(assemblyName); + } + + protected override Assembly Load(AssemblyName assemblyName) + { + string assemblyPath = dependencyResolver.ResolveAssemblyToPath(assemblyName); + + // When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher.Plugin + // Otherwise will get unexpected behaviour with plugins, e.g. JsonIgnore attribute not honored in WebSearch or other plugins + // that use Newtonsoft.Json + if (assemblyPath == null || ExistsInReferencedPluginPackage(assemblyName)) + return null; + + return LoadFromAssemblyPath(assemblyPath); + } + + internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type) + { + var allTypes = assembly.ExportedTypes; + + return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(type)); + } + + internal bool ExistsInReferencedPluginPackage(AssemblyName assemblyName) + { + return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null; + } + } +} diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 1025f9bae..224dbd85e 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -41,9 +41,9 @@ namespace Flow.Launcher.Core.Plugin { #if DEBUG - var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath); - var types = assembly.GetTypes(); - var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); + var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); + var assembly = assemblyLoader.LoadAssemblyAndDependencies(); + var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin)); var plugin = (IPlugin)Activator.CreateInstance(type); #else Assembly assembly = null; @@ -51,10 +51,10 @@ namespace Flow.Launcher.Core.Plugin try { - assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath); + var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); + assembly = assemblyLoader.LoadAssemblyAndDependencies(); - var types = assembly.GetTypes(); - var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); + var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin)); plugin = (IPlugin)Activator.CreateInstance(type); } diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index d834bd2d3..28d4c0e1f 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -49,13 +49,10 @@ - - - diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs index 5d7224c5b..80c6684f5 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -2,44 +2,91 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Windows.Media; namespace Flow.Launcher.Infrastructure.Image { [Serializable] + public class ImageUsage + { + + public int usage; + public ImageSource imageSource; + + public ImageUsage(int usage, ImageSource image) + { + this.usage = usage; + imageSource = image; + } + } + public class ImageCache { - private const int MaxCached = 5000; - public ConcurrentDictionary Usage = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _data = new ConcurrentDictionary(); + private const int MaxCached = 50; + public ConcurrentDictionary Data { get; private set; } = new ConcurrentDictionary(); + private const int permissibleFactor = 2; + public void Initialization(Dictionary usage) + { + foreach (var key in usage.Keys) + { + Data[key] = new ImageUsage(usage[key], null); + } + } public ImageSource this[string path] { get { - Usage.AddOrUpdate(path, 1, (k, v) => v + 1); - var i = _data[path]; - return i; + if (Data.TryGetValue(path, out var value)) + { + value.usage++; + return value.imageSource; + } + + return null; } - set { _data[path] = value; } - } + set + { + Data.AddOrUpdate( + path, + new ImageUsage(0, value), + (k, v) => + { + v.imageSource = value; + v.usage++; + return v; + } + ); - public Dictionary CleanupAndToDictionary() - => Usage - .OrderByDescending(o => o.Value) - .Take(MaxCached) - .ToDictionary(i => i.Key, i => i.Value); + // To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size + // This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time + if (Data.Count > permissibleFactor * MaxCached) + { + // To delete the images from the data dictionary based on the resizing of the Usage Dictionary. + + + foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key)) + { + if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon))) + { + Data.TryRemove(key, out _); + } + } + } + } + } public bool ContainsKey(string key) { - var contains = _data.ContainsKey(key); + var contains = Data.ContainsKey(key); return contains; } public int CacheSize() { - return _data.Count; + return Data.Count; } /// @@ -47,8 +94,8 @@ namespace Flow.Launcher.Infrastructure.Image /// public int UniqueImagesInCache() { - return _data.Values.Distinct().Count(); + return Data.Values.Select(x => x.imageSource).Distinct().Count(); } } -} +} \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 5cf3d84a6..edfb88cbf 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -13,12 +13,11 @@ namespace Flow.Launcher.Infrastructure.Image { public static class ImageLoader { - private static readonly ImageCache _imageCache = new ImageCache(); - private static readonly ConcurrentDictionary _guidToKey = new ConcurrentDictionary(); - private static readonly bool _enableHashImage = true; - + private static readonly ImageCache ImageCache = new ImageCache(); private static BinaryStorage> _storage; + private static readonly ConcurrentDictionary GuidToKey = new ConcurrentDictionary(); private static IImageHashGenerator _hashGenerator; + private static bool EnableImageHash = true; private static readonly string[] ImageExtensions = { @@ -36,25 +35,25 @@ namespace Flow.Launcher.Infrastructure.Image _storage = new BinaryStorage>("Image"); _hashGenerator = new ImageHashGenerator(); - _imageCache.Usage = LoadStorageToConcurrentDictionary(); + var usage = LoadStorageToConcurrentDictionary(); foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) { ImageSource img = new BitmapImage(new Uri(icon)); img.Freeze(); - _imageCache[icon] = img; + ImageCache[icon] = img; } Task.Run(() => { Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () => { - _imageCache.Usage.AsParallel().ForAll(x => + ImageCache.Data.AsParallel().ForAll(x => { Load(x.Key); }); }); - Log.Info($"|ImageLoader.Initialize|Number of preload images is <{_imageCache.Usage.Count}>, Images Number: {_imageCache.CacheSize()}, Unique Items {_imageCache.UniqueImagesInCache()}"); + Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}"); }); } @@ -62,13 +61,13 @@ namespace Flow.Launcher.Infrastructure.Image { lock (_storage) { - _storage.Save(_imageCache.CleanupAndToDictionary()); + _storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage)); } } private static ConcurrentDictionary LoadStorageToConcurrentDictionary() { - lock(_storage) + lock (_storage) { var loaded = _storage.TryLoad(new Dictionary()); @@ -106,11 +105,11 @@ namespace Flow.Launcher.Infrastructure.Image { if (string.IsNullOrEmpty(path)) { - return new ImageResult(_imageCache[Constant.MissingImgIcon], ImageType.Error); + return new ImageResult(ImageCache[Constant.MissingImgIcon], ImageType.Error); } - if (_imageCache.ContainsKey(path)) + if (ImageCache.ContainsKey(path)) { - return new ImageResult(_imageCache[path], ImageType.Cache); + return new ImageResult(ImageCache[path], ImageType.Cache); } if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) @@ -139,8 +138,8 @@ namespace Flow.Launcher.Infrastructure.Image Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e); Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2); - ImageSource image = _imageCache[Constant.MissingImgIcon]; - _imageCache[path] = image; + ImageSource image = ImageCache[Constant.MissingImgIcon]; + ImageCache[path] = image; imageResult = new ImageResult(image, ImageType.Error); } } @@ -191,7 +190,7 @@ namespace Flow.Launcher.Infrastructure.Image } else { - image = _imageCache[Constant.MissingImgIcon]; + image = ImageCache[Constant.MissingImgIcon]; path = Constant.MissingImgIcon; } @@ -218,27 +217,26 @@ namespace Flow.Launcher.Infrastructure.Image var img = imageResult.ImageSource; if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache) - { - // we need to get image hash - string hash = _enableHashImage ? _hashGenerator.GetHashFromImage(img) : null; + { // we need to get image hash + string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null; if (hash != null) { - if (_guidToKey.TryGetValue(hash, out string key)) - { - // image already exists - img = _imageCache[key]; + + if (GuidToKey.TryGetValue(hash, out string key)) + { // image already exists + img = ImageCache[key] ?? img; } else - { - // new guid - _guidToKey[hash] = path; + { // new guid + GuidToKey[hash] = path; } } // update cache - _imageCache[path] = img; + ImageCache[path] = img; } + return img; } diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 5dda76bc4..c5ab7dd3b 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -14,10 +14,10 @@ - 1.2.1 - 1.2.1 - 1.2.1 - 1.2.1 + 1.2.2 + 1.2.2 + 1.2.2 + 1.2.2 Flow.Launcher.Plugin Flow-Launcher MIT @@ -60,12 +60,9 @@ - + - - - \ No newline at end of file diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 1dd93b2ba..6196aa5df 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -53,6 +53,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitattributes = .gitattributes .gitignore = .gitignore appveyor.yml = appveyor.yml + Directory.Build.targets = Directory.Build.targets Scripts\flowlauncher.nuspec = Scripts\flowlauncher.nuspec LICENSE = LICENSE Scripts\post_build.ps1 = Scripts\post_build.ps1 diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 987a685ac..8548ba39e 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -72,23 +72,13 @@ - - - - all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index 13daddf10..85b745a6b 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -1,11 +1,13 @@  + Library netcoreapp3.1 {9B130CC5-14FB-41FF-B310-0A95B6894C37} Properties Flow.Launcher.Plugin.BrowserBookmark Flow.Launcher.Plugin.BrowserBookmark + true false false diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json index 7bb662b36..98db163ec 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json @@ -7,6 +7,6 @@ "Version": "1.2.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", - "ExecuteFileName": "Flow.Launcher.Plugin.browserBookmark.dll", + "ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll", "IcoPath": "Images\\bookmark.png" } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index e7cae42ae..9e1fefdb3 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -1,12 +1,14 @@ - + + Library netcoreapp3.1 {59BD9891-3837-438A-958D-ADC7F91F6F7E} Properties Flow.Launcher.Plugin.Caculator Flow.Launcher.Plugin.Caculator - true + true + true false false @@ -102,9 +104,7 @@ - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json index 316788884..16a46f4e5 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json @@ -4,7 +4,7 @@ "Name": "Calculator", "Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)", "Author": "cxfksword", - "Version": "1.0.1", + "Version": "1.0.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj b/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj index 19f8fb980..c7fe8271a 100644 --- a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj +++ b/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj @@ -1,11 +1,13 @@  + Library netcoreapp3.1 {F35190AA-4758-4D9E-A193-E3BDF6AD3567} Properties Flow.Launcher.Plugin.Color Flow.Launcher.Plugin.Color + true false false @@ -96,9 +98,4 @@ - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/plugin.json b/Plugins/Flow.Launcher.Plugin.Color/plugin.json index 938f8f853..dbba029c6 100644 --- a/Plugins/Flow.Launcher.Plugin.Color/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Color/plugin.json @@ -4,7 +4,7 @@ "Name": "Colors", "Description": "Provide hex color preview.(Try #000 in Flow Launcher)", "Author": "qianlifeng", - "Version": "1.0.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Color.dll", diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj index d1c185c36..699737634 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj @@ -1,11 +1,13 @@  + Library netcoreapp3.1 {1EE20B48-82FB-48A2-8086-675D6DDAB4F0} Properties Flow.Launcher.Plugin.ControlPanel Flow.Launcher.Plugin.ControlPanel + true false false @@ -96,9 +98,4 @@ - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json b/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json index 9a4b7ee0e..d7940125e 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json @@ -4,7 +4,7 @@ "Name": "Control Panel", "Description": "Search within the Control Panel.", "Author": "CoenraadS", - "Version": "1.0.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.ControlPanel.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index efa5339b4..a1a08843a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -5,6 +5,7 @@ netcoreapp3.1 true true + true false diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 7c6ef82b0..2c57ac668 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.2.4", + "Version": "1.2.5", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index 48639156e..e6bfa7aa3 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -1,11 +1,13 @@  + Library netcoreapp3.1 {FDED22C8-B637-42E8-824A-63B5B6E05A3A} Properties Flow.Launcher.Plugin.PluginIndicator Flow.Launcher.Plugin.PluginIndicator + true false false @@ -96,10 +98,5 @@ PreserveNewest - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json index 0c1cce6eb..7b2f66989 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json @@ -4,7 +4,7 @@ "Name": "Plugin Indicator", "Description": "Provide plugin actionword suggestion", "Author": "qianlifeng", - "Version": "1.0.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginIndicator.dll", diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj b/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj index 49451d5ba..08e89d861 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj @@ -1,12 +1,14 @@  + Library netcoreapp3.1 {049490F0-ECD2-4148-9B39-2135EC346EBE} Properties Flow.Launcher.Plugin.PluginManagement Flow.Launcher.Plugin.PluginManagement true + true false false @@ -97,10 +99,4 @@ - - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json index 5a8259f8f..3d73c2197 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json @@ -4,7 +4,7 @@ "Name": "Plugin Management", "Description": "Install/Remove/Update Flow Launcher plugins", "Author": "qianlifeng", - "Version": "1.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginManagement.dll", diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index ab84aa54a..cf9c96294 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -1,6 +1,7 @@  + Library netcoreapp3.1 Flow.Launcher.Plugin.ProcessKiller Flow.Launcher.Plugin.ProcessKiller @@ -8,6 +9,7 @@ https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller flow-launcher flow-plugin + true false false diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json b/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json index 894c49bdf..3e7ba4043 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json @@ -4,7 +4,7 @@ "Name":"Process Killer", "Description":"kill running processes from Flow", "Author":"Flow-Launcher", - "Version":"1.1.0", + "Version":"1.1.1", "Language":"csharp", "Website":"https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller", "IcoPath":"Images\\app.png", diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index 331566f90..3802297c7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -1,6 +1,7 @@  + Library netcoreapp3.1 {FDB3555B-58EF-4AE6-B5F1-904719637AB4} Properties @@ -8,6 +9,7 @@ Flow.Launcher.Plugin.Program true true + true false false @@ -107,10 +109,7 @@ - - - diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml index 579a64718..c266aa6f6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml @@ -40,7 +40,12 @@ Search programs in Flow Launcher Invalid Path - + + Customized Explorer + Args + You can customized the explorer used for opening the container folder by inputing the Environmental Variable of the explorer you want to use. It will be useful to use CMD to test whether the Environmental Variable is avaliable. + Enter the customized args you want to add for your customized explorer. %s for parent directory, %f for full path (which only works for win32). Check the explorer's website for details. + Success Successfully disabled this program from displaying in your query diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 9f3160746..8f124f3a4 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -71,21 +71,17 @@ namespace Flow.Launcher.Plugin.Program Win32[] win32; UWP.Application[] uwps; - lock (IndexLock) - { // just take the reference inside the lock to eliminate query time issues. - win32 = _win32s; - uwps = _uwps; - } + win32 = _win32s; + uwps = _uwps; - var results1 = win32.AsParallel() - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)); + var result = win32.Cast() + .Concat(uwps) + .AsParallel() + .Where(p => p.Enabled) + .Select(p => p.Result(query.Search, _context.API)) + .Where(r => r?.Score > 0) + .ToList(); - var results2 = uwps.AsParallel() - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)); - - var result = results1.Concat(results2).Where(r => r != null && r.Score > 0).ToList(); return result; } @@ -97,10 +93,9 @@ namespace Flow.Launcher.Plugin.Program public static void IndexWin32Programs() { var win32S = Win32.All(_settings); - lock (IndexLock) - { - _win32s = win32S; - } + + _win32s = win32S; + } public static void IndexUWPPrograms() @@ -109,10 +104,9 @@ namespace Flow.Launcher.Plugin.Program var support = Environment.OSVersion.Version.Major >= windows10.Major; var applications = support ? UWP.All() : new UWP.Application[] { }; - lock (IndexLock) - { - _uwps = applications; - } + + _uwps = applications; + } public static void IndexPrograms() diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs index b42acfbce..d4c96e5b7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs @@ -9,5 +9,6 @@ namespace Flow.Launcher.Plugin.Program.Programs string UniqueIdentifier { get; set; } string Name { get; } string Location { get; } + bool Enabled { get; } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 69e077ee2..3ea78156d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -265,27 +265,30 @@ namespace Flow.Launcher.Plugin.Program.Programs public Application(){} - private int Score(string query) - { - var displayNameMatch = StringMatcher.FuzzySearch(query, DisplayName); - var descriptionMatch = StringMatcher.FuzzySearch(query, Description); - var score = new[] { displayNameMatch.Score, descriptionMatch.Score }.Max(); - return score; - } public Result Result(string query, IPublicAPI api) { - var score = Score(query); - if (score <= 0) - { // no need to create result if score is 0 + var title = (Name, Description) switch + { + (var n, null) => n, + (var n, var d) when d.StartsWith(n) => d, + (var n, var d) when n.StartsWith(d) => n, + (var n, var d) when !string.IsNullOrEmpty(d) => $"{n}: {d}", + _ => Name + }; + + var matchResult = StringMatcher.FuzzySearch(query, title); + + if (!matchResult.Success) return null; - } var result = new Result { + Title = title, SubTitle = Package.Location, Icon = Logo, - Score = score, + Score = matchResult.Score, + TitleHighlightData = matchResult.MatchData, ContextData = this, Action = e => { @@ -294,23 +297,7 @@ namespace Flow.Launcher.Plugin.Program.Programs } }; - if (Description.Length >= DisplayName.Length && - Description.Substring(0, DisplayName.Length) == DisplayName) - { - result.Title = Description; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData; - } - else if (!string.IsNullOrEmpty(Description)) - { - var title = $"{DisplayName}: {Description}"; - result.Title = title; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData; - } - else - { - result.Title = DisplayName; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, DisplayName).MatchData; - } + return result; } @@ -324,7 +311,14 @@ namespace Flow.Launcher.Plugin.Program.Programs Action = _ => { - Main.StartProcess(Process.Start, new ProcessStartInfo(Package.Location)); + Main.StartProcess(Process.Start, + new ProcessStartInfo( + !string.IsNullOrEmpty(Main._settings.CustomizedExplorer) + ? Main._settings.CustomizedExplorer + : Settings.Explorer, + Main._settings.CustomizedArgs + .Replace("%s",$"\"{Package.Location}\"") + .Trim())); return true; }, diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index cdea767f3..092418b6c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -33,29 +33,30 @@ namespace Flow.Launcher.Plugin.Program.Programs private const string ApplicationReferenceExtension = "appref-ms"; private const string ExeExtension = "exe"; - private int Score(string query) - { - var nameMatch = StringMatcher.FuzzySearch(query, Name); - var descriptionMatch = StringMatcher.FuzzySearch(query, Description); - var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName); - var score = new[] { nameMatch.Score, descriptionMatch.Score, executableNameMatch.Score }.Max(); - return score; - } - public Result Result(string query, IPublicAPI api) { - var score = Score(query); - if (score <= 0) - { // no need to create result if this is zero + var title = (Name, Description) switch + { + (var n, null) => n, + (var n, var d) when d.StartsWith(n) => d, + (var n, var d) when n.StartsWith(d) => n, + (var n, var d) when !string.IsNullOrEmpty(d) => $"{n}: {d}", + _ => Name + }; + + var matchResult = StringMatcher.FuzzySearch(query, title); + + if (!matchResult.Success) return null; - } var result = new Result { + Title = title, SubTitle = FullPath, IcoPath = IcoPath, - Score = score, + Score = matchResult.Score, + TitleHighlightData = matchResult.MatchData, ContextData = this, Action = e => { @@ -72,24 +73,6 @@ namespace Flow.Launcher.Plugin.Program.Programs } }; - if (Description.Length >= Name.Length && - Description.Substring(0, Name.Length) == Name) - { - result.Title = Description; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData; - } - else if (!string.IsNullOrEmpty(Description)) - { - var title = $"{Name}: {Description}"; - result.Title = title; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData; - } - else - { - result.Title = Name; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData; - } - return result; } @@ -140,9 +123,20 @@ namespace Flow.Launcher.Plugin.Program.Programs Title = api.GetTranslation("flowlauncher_plugin_program_open_containing_folder"), Action = _ => { + var args = !string.IsNullOrWhiteSpace(Main._settings.CustomizedArgs) + ? Main._settings.CustomizedArgs + .Replace("%s",$"\"{ParentDirectory}\"") + .Replace("%f",$"\"{FullPath}\"") + : Main._settings.CustomizedExplorer==Settings.Explorer + ? $"/select,\"{FullPath}\"" + : Settings.ExplorerArgs; - - Main.StartProcess(Process.Start, new ProcessStartInfo("explorer", ParentDirectory)); + Main.StartProcess(Process.Start, + new ProcessStartInfo( + !string.IsNullOrWhiteSpace(Main._settings.CustomizedExplorer) + ? Main._settings.CustomizedExplorer + : Settings.Explorer, + args)); return true; }, @@ -255,9 +249,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var program = Win32Program(path); var info = FileVersionInfo.GetVersionInfo(path); if (!string.IsNullOrEmpty(info.FileDescription)) - { program.Description = info.FileDescription; - } return program; } catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) @@ -273,47 +265,23 @@ namespace Flow.Launcher.Plugin.Program.Programs { if (!Directory.Exists(directory)) return new string[] { }; - var files = new List(); - var folderQueue = new Queue(); - folderQueue.Enqueue(directory); - do + try { - var currentDirectory = folderQueue.Dequeue(); - try - { - foreach (var suffix in suffixes) - { - try - { - files.AddRange(Directory.EnumerateFiles(currentDirectory, $"*.{suffix}", SearchOption.TopDirectoryOnly)); - } - catch (DirectoryNotFoundException e) - { - ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" + - "|The directory trying to load the program from does not exist", e); - } - } - } - catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) - { - ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" + - $"|Permission denied when trying to load programs from {currentDirectory}", e); - } + var paths = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories) + .Where(x => suffixes.Contains(Extension(x))); + return paths; - try - { - foreach (var childDirectory in Directory.EnumerateDirectories(currentDirectory, "*", SearchOption.TopDirectoryOnly)) - { - folderQueue.Enqueue(childDirectory); - } - } - catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) - { - ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" + - $"|Permission denied when trying to load programs from {currentDirectory}", e); - } - } while (folderQueue.Any()); - return files; + } + catch (DirectoryNotFoundException e) + { + ProgramLogger.LogException($"Directory not found {directory}", e); + return new string[] { }; + } + catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) + { + ProgramLogger.LogException($"Permission denied {directory}", e); + return new string[] { }; + } } private static string Extension(string path) @@ -331,23 +299,20 @@ namespace Flow.Launcher.Plugin.Program.Programs private static ParallelQuery UnregisteredPrograms(List sources, string[] suffixes) { - var listToAdd = new List(); - sources.Where(s => Directory.Exists(s.Location) && s.Enabled) + var paths = sources.Where(s => Directory.Exists(s.Location) && s.Enabled) .SelectMany(s => ProgramPaths(s.Location, suffixes)) - .ToList() .Where(t1 => !Main._settings.DisabledProgramSources.Any(x => t1 == x.UniqueIdentifier)) - .ToList() - .ForEach(x => listToAdd.Add(x)); + .Distinct(); - var paths = listToAdd.Distinct().ToArray(); + var programs = paths.AsParallel().Select(x => Extension(x) switch + { + ExeExtension => ExeProgram(x), + ShortcutExtension => LnkProgram(x), + _ => Win32Program(x) + }); - var programs1 = paths.AsParallel().Where(p => Extension(p) == ExeExtension).Select(ExeProgram); - var programs2 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(LnkProgram); - var programs3 = from p in paths.AsParallel() - let e = Extension(p) - where e != ShortcutExtension && e != ExeExtension - select Win32Program(p); - return programs1.Concat(programs2).Concat(programs3); + + return programs; } private static ParallelQuery StartMenuPrograms(string[] suffixes) @@ -360,15 +325,16 @@ namespace Flow.Launcher.Plugin.Program.Programs var paths2 = ProgramPaths(directory2, suffixes); var toFilter = paths1.Concat(paths2); - var paths = toFilter - .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) - .Select(t1 => t1) - .Distinct() - .ToArray(); - var programs1 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(LnkProgram); - var programs2 = paths.AsParallel().Where(p => Extension(p) == ApplicationReferenceExtension).Select(Win32Program); - var programs = programs1.Concat(programs2).Where(p => p.Valid); + var programs = toFilter + .AsParallel() + .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) + .Distinct() + .Select(x => Extension(x) switch + { + ShortcutExtension => LnkProgram(x), + _ => Win32Program(x) + }).Where(x => x.Valid); return programs; } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs index fcb4cbf2d..7cb02a852 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs @@ -14,9 +14,15 @@ namespace Flow.Launcher.Plugin.Program public bool EnableStartMenuSource { get; set; } = true; public bool EnableRegistrySource { get; set; } = true; + public string CustomizedExplorer { get; set; } = Explorer; + public string CustomizedArgs { get; set; } = ExplorerArgs; internal const char SuffixSeperator = ';'; + internal const string Explorer = "explorer"; + + internal const string ExplorerArgs = "%s"; + /// /// Contains user added folder location contents as well as all user disabled applications /// diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml index 6051e0579..90fda1756 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml @@ -5,12 +5,13 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:program="clr-namespace:Flow.Launcher.Plugin.Program" mc:Ignorable="d" - d:DesignHeight="300" d:DesignWidth="600"> + d:DesignHeight="404.508" d:DesignWidth="600"> + @@ -24,7 +25,7 @@ - + @@ -71,9 +72,18 @@