From b4c61927f0d2324379c3fc3e498b2a11c44e26ae Mon Sep 17 00:00:00 2001 From: Alekhya Reddy Date: Wed, 24 Jun 2020 12:45:31 +1000 Subject: [PATCH 01/56] Remove Program Plugin dll (ShObjldITIb.dll) --- .../Programs/ApplicationActivationHelper.cs | 44 + .../Programs/ShellLinkHelper.cs | 132 +++ .../Programs/UWP.cs | 5 +- .../Programs/Win32.cs | 32 +- .../ShObjIdlTlb.dll | Bin 144896 -> 0 bytes installer/PowerToysSetup/Product.wxs | 937 ++++++++++++++++++ 6 files changed, 1129 insertions(+), 21 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs delete mode 100644 Plugins/Flow.Launcher.Plugin.Program/ShObjIdlTlb.dll create mode 100644 installer/PowerToysSetup/Product.wxs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs new file mode 100644 index 000000000..989995f31 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs @@ -0,0 +1,44 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace Microsoft.Plugin.Program.Programs +{ + public class ApplicationActivationHelper + { + // Reference : https://github.com/MicrosoftEdge/edge-launcher/blob/108e63df0b4cb5cd9d5e45aa7a264690851ec51d/MIcrosoftEdgeLauncherCsharp/Program.cs + public enum ActivateOptions + { + None = 0x00000000, + DesignMode = 0x00000001, + NoErrorUI = 0x00000002, + NoSplashScreen = 0x00000004, + } + + /// ApplicationActivationManager + [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IApplicationActivationManager + { + IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId); + IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); + IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); + } + + // Application Activation Manager Class + [ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] + public class ApplicationActivationManager : IApplicationActivationManager + { + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/] + public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public extern IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public extern IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs new file mode 100644 index 000000000..49977393a --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.IO; +using Accessibility; +using System.Runtime.InteropServices.ComTypes; +using System.Security.Policy; + +namespace Microsoft.Plugin.Program.Programs +{ + class ShellLinkHelper + { + [Flags()] + public enum SLGP_FLAGS + { + SLGP_SHORTPATH = 0x1, + SLGP_UNCPRIORITY = 0x2, + SLGP_RAWPATH = 0x4 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct WIN32_FIND_DATAW + { + public uint dwFileAttributes; + public long ftCreationTime; + public long ftLastAccessTime; + public long ftLastWriteTime; + public uint nFileSizeHigh; + public uint nFileSizeLow; + public uint dwReserved0; + public uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + public string cAlternateFileName; + } + + [Flags()] + public enum SLR_FLAGS + { + SLR_NO_UI = 0x1, + SLR_ANY_MATCH = 0x2, + SLR_UPDATE = 0x4, + SLR_NOUPDATE = 0x8, + SLR_NOSEARCH = 0x10, + SLR_NOTRACK = 0x20, + SLR_NOLINKINFO = 0x40, + SLR_INVOKE_MSI = 0x80 + } + + + // Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW + /// The IShellLink interface allows Shell links to be created, modified, and resolved + [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] + interface IShellLinkW + { + /// Retrieves the path and file name of a Shell link object + void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); + /// Retrieves the list of item identifiers for a Shell link object + void GetIDList(out IntPtr ppidl); + /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. + void SetIDList(IntPtr pidl); + /// Retrieves the description string for a Shell link object + void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + /// Sets the description for a Shell link object. The description can be any application-defined string + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + /// Retrieves the name of the working directory for a Shell link object + void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + /// Sets the name of the working directory for a Shell link object + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + /// Retrieves the command-line arguments associated with a Shell link object + void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + /// Sets the command-line arguments for a Shell link object + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + /// Retrieves the hot key for a Shell link object + void GetHotkey(out short pwHotkey); + /// Sets a hot key for a Shell link object + void SetHotkey(short wHotkey); + /// Retrieves the show command for a Shell link object + void GetShowCmd(out int piShowCmd); + /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. + void SetShowCmd(int iShowCmd); + /// Retrieves the location (path and index) of the icon for a Shell link object + void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + /// Sets the location (path and index) of the icon for a Shell link object + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + /// Sets the relative path to the Shell link object + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + /// Attempts to find the target of a Shell link, even if it has been moved or renamed + void Resolve(ref Accessibility._RemotableHandle hwnd, SLR_FLAGS fFlags); + /// Sets the path and file name of a Shell link object + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + [ComImport(), Guid("00021401-0000-0000-C000-000000000046")] + public class ShellLink + { + } + + // To initialize the app description + public String description = String.Empty; + + + // Retrieve the target path using Shell Link + public string retrieveTargetPath(string path) + { + var link = new ShellLink(); + const int STGM_READ = 0; + ((IPersistFile)link).Load(path, STGM_READ); + var hwnd = new _RemotableHandle(); + ((IShellLinkW)link).Resolve(ref hwnd, 0); + + const int MAX_PATH = 260; + StringBuilder buffer = new StringBuilder(MAX_PATH); + + var data = new WIN32_FIND_DATAW(); + ((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH); + var target = buffer.ToString(); + + // To set the app description + if (!String.IsNullOrEmpty(target)) + { + buffer = new StringBuilder(MAX_PATH); + ((IShellLinkW)link).GetDescription(buffer, MAX_PATH); + description = buffer.ToString(); + } + return target; + } + } +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index b8633f357..173cedd8d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -13,7 +13,6 @@ using System.Xml.Linq; using Windows.ApplicationModel; using Windows.Management.Deployment; using AppxPackaing; -using Shell; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.Program.Logger; using IStream = AppxPackaing.IStream; @@ -337,10 +336,10 @@ namespace Flow.Launcher.Plugin.Program.Programs private async void Launch(IPublicAPI api) { - var appManager = new ApplicationActivationManager(); + var appManager = new ApplicationActivationHelper.ApplicationActivationManager(); uint unusedPid; const string noArgs = ""; - const ACTIVATEOPTIONS noFlags = ACTIVATEOPTIONS.AO_NONE; + const ApplicationActivationHelper.ActivateOptions noFlags = ApplicationActivationHelper.ActivateOptions.None; await Task.Run(() => { try diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index cdea767f3..3b1da3abd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -8,13 +8,14 @@ using System.Security; using System.Text; using System.Threading.Tasks; using Microsoft.Win32; -using Shell; -using Flow.Launcher.Infrastructure; -using Flow.Launcher.Plugin.Program.Logger; -using Flow.Launcher.Plugin.SharedCommands; +using Wox.Infrastructure; +using Microsoft.Plugin.Program.Logger; +using Wox.Plugin; +using System.Reflection; -namespace Flow.Launcher.Plugin.Program.Programs +namespace Microsoft.Plugin.Program.Programs { + [Serializable] public class Win32 : IProgram { @@ -190,27 +191,21 @@ namespace Flow.Launcher.Plugin.Program.Programs var program = Win32Program(path); try { - var link = new ShellLink(); - const uint STGM_READ = 0; - ((IPersistFile)link).Load(path, STGM_READ); - var hwnd = new _RemotableHandle(); - link.Resolve(ref hwnd, 0); - const int MAX_PATH = 260; StringBuilder buffer = new StringBuilder(MAX_PATH); + ShellLinkHelper _helper = new ShellLinkHelper(); + string target = _helper.retrieveTargetPath(path); - var data = new _WIN32_FIND_DATAW(); - const uint SLGP_SHORTPATH = 1; - link.GetPath(buffer, buffer.Capacity, ref data, SLGP_SHORTPATH); - var target = buffer.ToString(); if (!string.IsNullOrEmpty(target)) { var extension = Extension(target); if (extension == ExeExtension && File.Exists(target)) { - buffer = new StringBuilder(MAX_PATH); - link.GetDescription(buffer, MAX_PATH); - var description = buffer.ToString(); + program.LnkResolvedPath = program.FullPath; + program.FullPath = Path.GetFullPath(target).ToLower(); + program.ExecutableName = Path.GetFileName(target); + + var description = _helper.description; if (!string.IsNullOrEmpty(description)) { program.Description = description; @@ -276,6 +271,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var files = new List(); var folderQueue = new Queue(); folderQueue.Enqueue(directory); + do { var currentDirectory = folderQueue.Dequeue(); diff --git a/Plugins/Flow.Launcher.Plugin.Program/ShObjIdlTlb.dll b/Plugins/Flow.Launcher.Plugin.Program/ShObjIdlTlb.dll deleted file mode 100644 index 83815e40a4380f97638c8c0af35427eb4ae808e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 144896 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
#2?NVUwg`D`1PSuV5rEjE7A#&LPJA1M7~+PKWd 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 D=|-o! diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs new file mode 100644 index 000000000..868acfbf1 --- /dev/null +++ b/installer/PowerToysSetup/Product.wxs @@ -0,0 +1,937 @@ + + + + + + + + + + + + + + + + + + + + + + = 17134)]]> + + + + + + + + + + + + + + + NOT Installed + 1 + NOT Installed + NOT Installed + Installed AND _REMOVE_ALL="Yes" + Installed AND _REMOVE_ALL="Yes" + Installed AND NOT (_REMOVE_ALL="Yes") + Installed AND NOT (_REMOVE_ALL="Yes") + + + + + + + + + + + + + + + + + + + + + + NOT Installed and CREATESCHEDULEDTASK = 1 + + + + + NOT Installed + + + Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") + + + + NOT Installed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSTALLDESKTOPSHORTCUT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4f8ca9a453033187a71147758d378f4737d09d01 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 24 Jun 2020 12:55:21 +1000 Subject: [PATCH 02/56] update commit for Flow-Launcher --- .../Programs/ApplicationActivationHelper.cs | 2 +- .../Programs/ShellLinkHelper.cs | 2 +- .../Programs/Win32.cs | 10 +- installer/PowerToysSetup/Product.wxs | 937 ------------------ 4 files changed, 7 insertions(+), 944 deletions(-) delete mode 100644 installer/PowerToysSetup/Product.wxs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs index 989995f31..3023836c1 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.RegularExpressions; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { public class ApplicationActivationHelper { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs index 49977393a..4ded3412a 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs @@ -7,7 +7,7 @@ using Accessibility; using System.Runtime.InteropServices.ComTypes; using System.Security.Policy; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { class ShellLinkHelper { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 3b1da3abd..f9fcc0897 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -8,12 +8,11 @@ using System.Security; using System.Text; using System.Threading.Tasks; using Microsoft.Win32; -using Wox.Infrastructure; -using Microsoft.Plugin.Program.Logger; -using Wox.Plugin; -using System.Reflection; +using Flow.Launcher.Infrastructure; +using Flow.Launcher.Plugin.Program.Logger; +using Flow.Launcher.Plugin.SharedCommands; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { [Serializable] @@ -23,6 +22,7 @@ namespace Microsoft.Plugin.Program.Programs public string UniqueIdentifier { get; set; } public string IcoPath { get; set; } public string FullPath { get; set; } + public string LnkResolvedPath { get; set; } public string ParentDirectory { get; set; } public string ExecutableName { get; set; } public string Description { get; set; } diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs deleted file mode 100644 index 868acfbf1..000000000 --- a/installer/PowerToysSetup/Product.wxs +++ /dev/null @@ -1,937 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - = 17134)]]> - - - - - - - - - - - - - - - NOT Installed - 1 - NOT Installed - NOT Installed - Installed AND _REMOVE_ALL="Yes" - Installed AND _REMOVE_ALL="Yes" - Installed AND NOT (_REMOVE_ALL="Yes") - Installed AND NOT (_REMOVE_ALL="Yes") - - - - - - - - - - - - - - - - - - - - - - NOT Installed and CREATESCHEDULEDTASK = 1 - - - - - NOT Installed - - - Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") - - - - NOT Installed - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - INSTALLDESKTOPSHORTCUT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 0342693442d5cce41c1ebd8537adbe9504116bbd Mon Sep 17 00:00:00 2001 From: Alekhya Reddy Date: Wed, 24 Jun 2020 21:34:33 +1000 Subject: [PATCH 03/56] Remove Program Plugin dll (AppxPackagingTlb.dll) --- .../AppxPackagingTlb.dll | Bin 17408 -> 0 bytes .../Flow.Launcher.Plugin.Program.csproj | 11 - .../Programs/AppxPackageHelper.cs | 82 ++ .../Programs/UWP.cs | 69 +- installer/PowerToysSetup/Product.wxs | 951 ++++++++++++++++++ 5 files changed, 1076 insertions(+), 37 deletions(-) delete mode 100644 Plugins/Flow.Launcher.Plugin.Program/AppxPackagingTlb.dll create mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs create mode 100644 installer/PowerToysSetup/Product.wxs diff --git a/Plugins/Flow.Launcher.Plugin.Program/AppxPackagingTlb.dll b/Plugins/Flow.Launcher.Plugin.Program/AppxPackagingTlb.dll deleted file mode 100644 index 183cfc085c378e7b1927d3354d65d7206e15c978..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17408 zcmeHudzf3*b?-i!8I9zzhk4k+#DvJPDW)K!$I+`Hj*recvK7w@Ni%-LMb^xmnNiS4 zgQUTpgxpLp1WJMHToOYI2@vxLA(T*{At3=b&YRGNo1}qT`dwQ3$SvV(`axUTwBi2N zI!Ak?8IO~%U;pXX%3i;-e`~M3_S)<0bI#sJqrn5mM4J$z9rD&&LOh8heReXQ85U99 z{mw6Ui^sjsZGKW2cy4oUrdadMRV&lg!mMwiP%c;MzH!S}oiF=}WuGye_03i$t?ld9 zt=a0h9x;U&P};;#qmS)v^!BoFZSGRKgt!zMx2@fA07oC>5QL)h;o!34Mv3F6qqrLa zJ$>55&g+Ee;r~lVhuS9Mr~8E%rt%@^DG|#*i$bhjuKQKEPY6$wod_kGgSFKV7h8*i6D#=$8()8K6LW8a; zulP`GF3}-Id_vrr5JDm4zvGk4zAvdbttpnP)a`-o;XpVDp920%z>{wmV$1&!;^Iw0 z%xpk8Tdx+&(>1dAM8MA5meH)Z6J?w|v1Qk2+Q9Mi;6FoBw-KKB?z4liYWsbfgFW&KuAyHwa1*%zKjiX@C?gyv9?IrD zNW&U&HtTG*)nRU-pe_J*4(!})sX29PndKaI7F$j^mS=I@hn>29X2%@1f!SvqcDC@M zi_d{Y(8Ig9?j+Z>IqU}xyMXKd#$f?wKX;hQY@I@5XcJLp?{wI^nI#;yLnt@_JHXPg z+{PTKXO=| z*=r8FirL0CyY~fV35Q+HY{Fq5VRpU4u3>h+!>(iYjKi*H_WvAqBQsCC-NVhyf(|>z zY{X&5F$=?qIO782F~$cOPcpv9_$uRDjJ@3%WhtI9j)(BTTEZ(CZ)E&5;}eX}GX5Fk z>x>=i$bK`U%9v&xXIx-B#`qxPNyZl$UuArYvA26n#&co!0r*TYT8!5*eg@cx`Rm=6 z!{)CUJ!et*B4DF7bJjRCM;Py6{4V1gz=q8^JyWoWF^)3UfQ{Nu_Iv=fUjsHYKkX?) zv$mHo%vfMN%6Kp1DaJo%?CK*+m2r%*25fk{sjrIK2N}P@_?9t9TFJTDNCt|N;J>J&@7MJ+Ynf7^K7xq$%*spuv6z`XHA@5t_Hzj+ww?nyH zGHREXEbU#R9Fi=AeA??E@;U11RhAfKX;NC==s8ERB&&MAET$y8&ij~{mW=XJl>=+~g;rPg_+Iq-m5O9kBkuqLX^OLobc9m-+JhQW?Vb_LjiWH*6* zP_oZ}eMqvW!LF9<1+WiG_P1bIUgAS}Y46&hd_=Ohfh|fF2m7dG17O!kHV#HBMBB?E z*tL@VF4%RFJ>F#~ACv5RVAo6b64(ur{S554Bs<|Tlp7`Uu6>{K+mem<-=f?s+2Q_M zm0KkH7})=m?3Vu9lw*?7Ir_L{$NTS4{+DF;^xvc0D%m4opOEay{`-_qO7^w>hm_-z zQGd5d_9FVbU9#8uA5nfsvg7?vD0fIk{oN@U_4g^sj`u&Md|EQHq*c0|YqGpsGVl67 zRPK@N-1QGBCnTfD?v-r&`fn-sNw#DCt;+q9?OA`D@_=MxsQavBlk2~yJSf?RQ1_5z zw}5?KvJObuxMXDcgk)s-2a=KH7bPQ0 zdw)*znk>I0>yG!oqQ}O%R#&s1>)6jN?B{;;rHH3n{JXFkoZ{N=GQR56Qf&Wl8VyC*=b;EUd0g=3_t@vi zIa8>eay0fxn`=)~<{8kUXAQUYw&;CSv6@d?Pg#Ax(~2@sJdFq2=c<0t>M5@aeolEQ z1J46lgYmw)LWAYVGMmk7C?`7@4>B$=9%p=n@w<#KGyWsv+AeDKYm6G>7~>q{O^o+< zQRYuF{*dvn8Q)|SYiUHiYh8%*ZEGo0`1qQTi>J{v>sOXpL!N)p@3ggTRx z$I#~->99(f{Limn85^G?=L|NNn>@Dqv@4o>IkTqOlFKct!>4wMHOE0fcqCpFR;yZ4wp9ry{-iCELR%1!L=W_ z$#o@gv+JY43tYbg`~_A5w9^YRda$eWyFBntxKV_F=ZYB6CoTnU6&i4xNC7Vvdw>Bk z0#wB)FfI-NFBN&~E6jPfCI4RBIi51e6~;~46U zM>vwJIg&+=CvmD9iIfgGVKF%>b$@mqH z$+e8@ zS-(-COrEPy-h2vWY#Zw@W_y(}&i0ool=+(D1h8Lx8@NgQ$fX0{0&Wo(woPz(0z2Ju+ffk~ z7I2dY0WT5<+fJbTaUfZq;_@k2{#Dy6C_e=x`&ZbqP1z*6+uIbf+=6nveG8X$l*ik3 zE+0ktruL&;KH=ad@dxdvpnn-iZC`P4lUUQy<|0j-Ym-nrwg4{&l16uMlenRyt$mYt z9?1PVxJmrHL+7%N+VkBLz+oWS97TD-eU!^5P(I;4;gs=Sz;%ktr=Wk%{R)>~LHVED zZ5^A$=FYYb?#01PVo#^eWgWHiofE*jfZPkp-|0NcF`8UX05Jh>+)duN+bV9t4F38ym@})fLdM9KH%I8Baf?NU~!7&DT4;r79p9t%#6s7w(@#m zI-ApS+1xI{A?MPArZZ`ZC2OfvGt2X{R<(e6WGfqKPf4?8&8p^XbmrRDQ|-)x^yD&i zwK8W_>qX0%tBv#Mm>brdRi3oU6EIjFdBZeg)hhGViPa4Z>#E{}r5EN3~2>QK%OyYuf? zr^~n>ROYf)m2z7Xj{TTbts!|!&34TfCzlwvnkr0KWWJ=qrq znq*Vl^yVk(1BHdke0|Br=6j0eNkOLs-OOsGVxcxvm~9#?WrV8qO8F`~TLYC^Q%`!; zD%36K^g6dbbTc~SW}q-Zf{>9}>mI#t@_H|U)ncwxYL>vc;^&LVShJP7MIOc6RbpaX%uS3lvnx2g zq@x17I`&!1x7iJ-5nRnHbbXbD@*Mcb`j znOn$J#9Xb;lVG-37t_@#oO6`y8JMwf?(1RzkGzaEP0rwCcxnpg7X6W$$2VH0)H@d0 zdZA7}3-wxknjDgX+|i)RmYC!IrYg7<9w^R*Lt>^-n;Ep~GnGlP%c?g{DwR`}N_`H0 zQB&vC*6GTDwmIFcv_{p9iDa8%^)YSXZcc_v_i%2;Rn2#9GHSm45|?*DA}jCEwqL7? z^+pltwnUjG8cdt+uN;*#DN(~?agJQIdujYjlgYWT#2l@krY)yA%`VP3+B<1*4B1nw zies~zr*ZpD4~O2wkRdSPsr9Lz|tLv1aWX8GCBt zs2#0YRXWRdFOoSmeEZ{8RKpB4G$Ni&IBl{F{Rkefd@AV(ZudkQrv%*rzLX~qH;(pv zN!QFPv3?M99RHL}uA&E4KHsv=&1=c*@W5!!%#UW$tu>gHSd{0nOw8n3YqZha?%_-t zt7N#BOXj?h&W;Rd`&W80h6lCuP^+=IFJ}(XI=WSz%4oX=&7ox-?jFuA(~M}@EYI|o z2u8Hr?pA-K$!kMK{<2Xsvn<}zs508#mEmXg-R7W~AJVWyZs~3`Yi6**P7R+%z0%sr z=YY0+EHpZM%h2MTujJFEe`I+139%z=jpD%Y(6VrChur@vo0fv@H}JBn@BlErEEOZh=D}rgshTE@^cW zeb_L0*TfV2mu%v&cgi#(T79dxEc7!NwvONohRX(W2Fs=MeY(A7eqTIl=i;=rTE;n_ zr>)%=RnO3N$Q;dO8uP^o@N~P`JaRmc*0rVG^qGvY`yL+6=#5$0*aPq9oo;5x%;oig zG~Gb#nYL;@UOHsPurXe4hF$!ahT9=L;r_D6@wS?^F|3c$?JAoeNGCHI?l3F-rFl=k zy1`i5Fo##K9vsc4^;Ny2Ycqq8vu2)d%AhTYl&=W(=d-)DjA`WgmZjqjM><88cyq8k z;EXvqJfjrqN;;R|t=LMRR?4D5vSUm2V{SqeIydQ%Aq1vEp?_KCx02 z44g(66xi)zpIr4!ga&dY_Qao|7E?>5ZVCB*-1GMpE=fJ3XzFMVa z4T@NRH5hFdhUl;Mq2}c_FAEWBp+t+*-TVfu;bOGh*u7YjtZQt)*c3}wL5tF*fxy~9AEJUw5S z=3ukP6BmqqDeQqUgaK@mN<0pGx!qWzQ;HhBJG&-yz2dz|ZRt%Z_HWa<()bF+_Q_HS z?^)Y?^tM&JvE$nN=D#!YzMGF5f8Y7~u64rm#Sb1FJ4b!_x4b@ApRaFQPw&NUpVAlT z+eQb~KE(UBb)p)FZun8Cd}-gdF0$BZTNr)YyuNm&ySq2nx2?mcD1F;JzBZ+&!`tKa zqR>0%?dh#kMQ?o_4EwhA)}g@vcIdoh+gtCgd*NE?={@3wYo(`eo6C)FI{J3E`$S*7 z6TK}i_U@xz7Z46g<>-u*0Al}{q$Ot3{*#|iYIRu%6 z%s@&m_ZpYG%jNENxxL_Z$kmX`DC`_EOj+*Y&KFTaHyG+Ml)UK5L-_^2f$oJe1xIG?d=Wj29C21-bklMzft} zP&>WcZ+8*25s*yh1KjN|E$8TEbPc#~z3l*`|h=n2%H5~JY)l|x_ z#*Lsq84E@I(Lm79Rl`(u%@BetlV&oZCISh+sYZf+H5^a+lMyZEH)07h7L6ri5hIQt z$TT{jCXu)r(t>KhpGqa*JQ$7os zzZs3G(GbpFDjBeSYT<-w#xSC&7K+i>V}31~3i*TKL{LqdsYpBl%kva*PRq$`-h$bV z5ci%LNJN~0x?RGp7aN`xQ;}q zp>QB#TSk*=D3OdL{o$yIB;$JXClg3bK*xCvCDnu(bf!ftqNlD|AF@4p98pV0=3$as#c`$B zy+;fUBTS(R7b#{)5}Bu~E{6H1#-q58n9lT#o2jT~q)b@S+0-zX5@ECnrF8VIs(QR} zNraPhi6(Hz#pJi|xCs*%ToTSSJ+2!u!^u(#$&Ex!gqWawn!$uW8NwBWNuvg$1_qCJ zyPTznsvChQ=30m*u^OO!nnuVUi^MeKMKjU8llee2g_||97!G32qYg6!6FVA?o3Sv? zXi|lxSa1ITf$zrKsqEw83TXT$)UL?um8wxH4br$Z{qKHZ|ILKL?Hcf2r_hdLlUvr(Tvp(wxasLY#ZGr7j%{CXaO(Svx9%^EO67N zW3a8ldTA^{u^l!I!Efos2G~TFv^-=(GqG=VQI#1Z`6VF7(!0i1TmpM%{vJWb+A zn(>LG1A?+1Z(&M7vqEtD+amj%}Oc_=X)HlzED; zkqcs!QGs8E{pxYrIUj+~3ZkFKxav(Kw)?fm(rU9jn?4HizuITS{!RSO-}tXK@c$k! HW#InPreserveNewest - - - - .\AppxPackagingTlb.dll - True - - - .\ShObjIdlTlb.dll - True - - diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs new file mode 100644 index 000000000..f9605a574 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Windows.Storage; + +namespace Microsoft.Plugin.Program.Programs +{ + public class AppxPackageHelper + { + // This function returns a list of attributes of applications + public List getAppsFromManifest(IStream stream) + { + List apps = new List(); + var appxFactory = new AppxFactory(); + var reader = ((IAppxFactory)appxFactory).CreateManifestReader(stream); + var manifestApps = reader.GetApplications(); + while (manifestApps.GetHasCurrent()) + { + string appListEntry; + var manifestApp = manifestApps.GetCurrent(); + manifestApp.GetStringValue("AppListEntry", out appListEntry); + if (appListEntry != "none") + { + apps.Add(manifestApp); + } + manifestApps.MoveNext(); + } + return apps; + } + + // Reference : https://stackoverflow.com/questions/32122679/getting-icon-of-modern-windows-app-from-a-desktop-application + [Guid("5842a140-ff9f-4166-8f5c-62f5b7b0c781"), ComImport] + public class AppxFactory + { + } + + [Guid("BEB94909-E451-438B-B5A7-D79E767B75D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxFactory + { + void _VtblGap0_2(); // skip 2 methods + IAppxManifestReader CreateManifestReader(IStream inputStream); + } + + [Guid("4E1BD148-55A0-4480-A3D1-15544710637C"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxManifestReader + { + void _VtblGap0_1(); // skip 1 method + IAppxManifestProperties GetProperties(); + void _VtblGap1_5(); // skip 5 methods + IAppxManifestApplicationsEnumerator GetApplications(); + } + + [Guid("9EB8A55A-F04B-4D0D-808D-686185D4847A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxManifestApplicationsEnumerator + { + IAppxManifestApplication GetCurrent(); + bool GetHasCurrent(); + bool MoveNext(); + } + + [Guid("5DA89BF4-3773-46BE-B650-7E744863B7E8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxManifestApplication + { + [PreserveSig] + int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value); + + [PreserveSig] + int GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string value); + } + + [Guid("03FAF64D-F26F-4B2C-AAF7-8FE7789B8BCA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxManifestProperties + { + [PreserveSig] + int GetBoolValue([MarshalAs(UnmanagedType.LPWStr)]string name, out bool value); + [PreserveSig] + int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index b8633f357..1706bb606 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -12,14 +12,18 @@ using System.Windows.Media.Imaging; using System.Xml.Linq; using Windows.ApplicationModel; using Windows.Management.Deployment; -using AppxPackaing; -using Shell; -using Flow.Launcher.Infrastructure; -using Flow.Launcher.Plugin.Program.Logger; -using IStream = AppxPackaing.IStream; +using Wox.Infrastructure; +using Microsoft.Plugin.Program.Logger; using Rect = System.Windows.Rect; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Media; +using System.Windows.Controls; +using Wox.Plugin; +using System.Reflection; +using Wox.Plugin.SharedCommands; +using System.Runtime.InteropServices.ComTypes; -namespace Flow.Launcher.Plugin.Program.Programs +namespace Microsoft.Plugin.Program.Programs { [Serializable] public class UWP @@ -51,12 +55,12 @@ namespace Flow.Launcher.Plugin.Program.Programs private void InitializeAppInfo() { + AppxPackageHelper _helper = new AppxPackageHelper(); var path = Path.Combine(Location, "AppxManifest.xml"); var namespaces = XmlNamespaces(path); InitPackageVersion(namespaces); - var appxFactory = new AppxFactory(); IStream stream; const uint noAttribute = 0x80; const Stgm exclusiveRead = Stgm.Read | Stgm.ShareExclusive; @@ -64,20 +68,15 @@ namespace Flow.Launcher.Plugin.Program.Programs if (hResult == Hresult.Ok) { - var reader = appxFactory.CreateManifestReader(stream); - var manifestApps = reader.GetApplications(); var apps = new List(); - while (manifestApps.GetHasCurrent() != 0) + + List _apps = _helper.getAppsFromManifest(stream); + foreach(var _app in _apps) { - var manifestApp = manifestApps.GetCurrent(); - var appListEntry = manifestApp.GetStringValue("AppListEntry"); - if (appListEntry != "none") - { - var app = new Application(manifestApp, this); - apps.Add(app); - } - manifestApps.MoveNext(); + var app = new Application(_app, this); + apps.Add(app); } + Apps = apps.Where(a => a.AppListEntry != "none").ToArray(); } else @@ -356,13 +355,30 @@ namespace Flow.Launcher.Plugin.Program.Programs }); } - public Application(IAppxManifestApplication manifestApp, UWP package) + public Application(AppxPackageHelper.IAppxManifestApplication manifestApp, UWP package) { - UserModelId = manifestApp.GetAppUserModelId(); - UniqueIdentifier = manifestApp.GetAppUserModelId(); - DisplayName = manifestApp.GetStringValue("DisplayName"); - Description = manifestApp.GetStringValue("Description"); - BackgroundColor = manifestApp.GetStringValue("BackgroundColor"); + // This is done because we cannot use the keyword 'out' along with a property + string tmpUserModelId; + string tmpUniqueIdentifier; + string tmpDisplayName; + string tmpDescription; + string tmpBackgroundColor; + string tmpEntryPoint; + + manifestApp.GetAppUserModelId(out tmpUserModelId); + manifestApp.GetAppUserModelId(out tmpUniqueIdentifier); + manifestApp.GetStringValue("DisplayName", out tmpDisplayName); + manifestApp.GetStringValue("Description", out tmpDescription); + manifestApp.GetStringValue("BackgroundColor", out tmpBackgroundColor); + manifestApp.GetStringValue("EntryPoint", out tmpEntryPoint); + + UserModelId = tmpUserModelId; + UniqueIdentifier = tmpUniqueIdentifier; + DisplayName = tmpDisplayName; + Description = tmpDescription; + BackgroundColor = tmpBackgroundColor; + EntryPoint = tmpEntryPoint; + Package = package; DisplayName = ResourceFromPri(package.FullName, package.Name, DisplayName); @@ -430,7 +446,7 @@ namespace Flow.Launcher.Plugin.Program.Programs return $"{prefix}//{packageName}{key}"; } - internal string LogoUriFromManifest(IAppxManifestApplication app) + internal string LogoUriFromManifest(AppxPackageHelper.IAppxManifestApplication app) { var logoKeyFromVersion = new Dictionary { @@ -440,8 +456,9 @@ namespace Flow.Launcher.Plugin.Program.Programs }; if (logoKeyFromVersion.ContainsKey(Package.Version)) { + string logoUri; var key = logoKeyFromVersion[Package.Version]; - var logoUri = app.GetStringValue(key); + app.GetStringValue(key, out logoUri); return logoUri; } else diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs new file mode 100644 index 000000000..2ff4c033f --- /dev/null +++ b/installer/PowerToysSetup/Product.wxs @@ -0,0 +1,951 @@ + + + + + + + + + + + + + + + + + + + + + + = 17134)]]> + + + + + + + + + + + + + + + NOT Installed + 1 + NOT Installed + NOT Installed + Installed AND _REMOVE_ALL="Yes" + Installed AND _REMOVE_ALL="Yes" + Installed AND NOT (_REMOVE_ALL="Yes") + Installed AND NOT (_REMOVE_ALL="Yes") + + + + + + + + + + + + + + + + + + + + + + NOT Installed and CREATESCHEDULEDTASK = 1 + + + + + NOT Installed + + + Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") + + + + NOT Installed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSTALLDESKTOPSHORTCUT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 327e2f32531afdffd9262a1970773a7055b5b6e0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 24 Jun 2020 21:55:52 +1000 Subject: [PATCH 04/56] update commit for Flow-Launcher --- .../Programs/AppxPackageHelper.cs | 2 +- .../Programs/UWP.cs | 15 +- installer/PowerToysSetup/Product.wxs | 951 ------------------ 3 files changed, 6 insertions(+), 962 deletions(-) delete mode 100644 installer/PowerToysSetup/Product.wxs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs index f9605a574..e48f30757 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Windows.Storage; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { public class AppxPackageHelper { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 1706bb606..6818a8bcd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Security.Principal; using System.Text; using System.Threading.Tasks; @@ -12,18 +13,11 @@ using System.Windows.Media.Imaging; using System.Xml.Linq; using Windows.ApplicationModel; using Windows.Management.Deployment; -using Wox.Infrastructure; -using Microsoft.Plugin.Program.Logger; +using Flow.Launcher.Infrastructure; +using Flow.Launcher.Plugin.Program.Logger; using Rect = System.Windows.Rect; -using Windows.UI.Xaml.Media.Imaging; -using Windows.UI.Xaml.Media; -using System.Windows.Controls; -using Wox.Plugin; -using System.Reflection; -using Wox.Plugin.SharedCommands; -using System.Runtime.InteropServices.ComTypes; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { [Serializable] public class UWP @@ -253,6 +247,7 @@ namespace Microsoft.Plugin.Program.Programs public string UserModelId { get; set; } public string BackgroundColor { get; set; } + public string EntryPoint { get; set; } public string Name => DisplayName; public string Location => Package.Location; diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs deleted file mode 100644 index 2ff4c033f..000000000 --- a/installer/PowerToysSetup/Product.wxs +++ /dev/null @@ -1,951 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - = 17134)]]> - - - - - - - - - - - - - - - NOT Installed - 1 - NOT Installed - NOT Installed - Installed AND _REMOVE_ALL="Yes" - Installed AND _REMOVE_ALL="Yes" - Installed AND NOT (_REMOVE_ALL="Yes") - Installed AND NOT (_REMOVE_ALL="Yes") - - - - - - - - - - - - - - - - - - - - - - NOT Installed and CREATESCHEDULEDTASK = 1 - - - - - NOT Installed - - - Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") - - - - NOT Installed - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - INSTALLDESKTOPSHORTCUT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 9c7dc054defe73dbdd917e16ef7d341be1dfaa5a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 25 Jun 2020 06:34:30 +1000 Subject: [PATCH 05/56] remove reference of dll in project file --- .../Flow.Launcher.Plugin.Program.csproj | 4 ---- 1 file changed, 4 deletions(-) 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..fb3a894db 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -44,10 +44,6 @@ .\AppxPackagingTlb.dll True - - .\ShObjIdlTlb.dll - True - From b52dbbea53e7181e4eef49aeccbe7d3ab5de7d6e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 19 Aug 2020 21:02:46 +1000 Subject: [PATCH 06/56] launch win32 lnk programs using LnkResolvedPath use LnkResolvedPath which is the original lnk path, rather than using the FullPath property which is assigned with the path to the actual exe --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f9fcc0897..f0de9f2c2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -54,7 +54,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var result = new Result { - SubTitle = FullPath, + SubTitle = LnkResolvedPath ?? FullPath, IcoPath = IcoPath, Score = score, ContextData = this, @@ -62,7 +62,7 @@ namespace Flow.Launcher.Plugin.Program.Programs { var info = new ProcessStartInfo { - FileName = FullPath, + FileName = LnkResolvedPath ?? FullPath, WorkingDirectory = ParentDirectory, UseShellExecute = true }; From d3e88c478a19e2638d461676f3d6648269b2a9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 19 Dec 2020 14:09:16 +0800 Subject: [PATCH 07/56] Add Programs Hasher and use IEnumerable instead of ParallelQuery since it performs better. --- .../Programs/Win32.cs | 81 +++++++++++++------ 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 2441b59f4..0f976cd58 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -11,6 +10,9 @@ using Microsoft.Win32; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.Program.Logger; using Flow.Launcher.Plugin.SharedCommands; +using Flow.Launcher.Infrastructure.Logger; +using System.Diagnostics; +using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Plugin.Program.Programs { @@ -53,6 +55,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var result = new Result { + Title = title, SubTitle = LnkResolvedPath ?? FullPath, IcoPath = IcoPath, Score = matchResult.Score, @@ -262,10 +265,10 @@ namespace Flow.Launcher.Plugin.Program.Programs try { var paths = Directory.EnumerateFiles(directory, "*", new EnumerationOptions - { - IgnoreInaccessible = true, - RecurseSubdirectories = true - }) + { + IgnoreInaccessible = true, + RecurseSubdirectories = true + }) .Where(x => suffixes.Contains(Extension(x))); return paths; @@ -295,14 +298,14 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - private static ParallelQuery UnregisteredPrograms(List sources, string[] suffixes) + private static IEnumerable UnregisteredPrograms(List sources, string[] suffixes) { var paths = sources.Where(s => Directory.Exists(s.Location) && s.Enabled) .SelectMany(s => ProgramPaths(s.Location, suffixes)) .Where(t1 => !Main._settings.DisabledProgramSources.Any(x => t1 == x.UniqueIdentifier)) .Distinct(); - var programs = paths.AsParallel().Select(x => Extension(x) switch + var programs = paths.Select(x => Extension(x) switch { ExeExtension => ExeProgram(x), ShortcutExtension => LnkProgram(x), @@ -313,7 +316,7 @@ namespace Flow.Launcher.Plugin.Program.Programs return programs; } - private static ParallelQuery StartMenuPrograms(string[] suffixes) + private static IEnumerable StartMenuPrograms(string[] suffixes) { var disabledProgramsList = Main._settings.DisabledProgramSources; @@ -325,7 +328,6 @@ namespace Flow.Launcher.Plugin.Program.Programs var toFilter = paths1.Concat(paths2); var programs = toFilter - .AsParallel() .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) .Distinct() .Select(x => Extension(x) switch @@ -336,32 +338,31 @@ namespace Flow.Launcher.Plugin.Program.Programs return programs; } - private static ParallelQuery AppPathsPrograms(string[] suffixes) + private static IEnumerable AppPathsPrograms(string[] suffixes) { // https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121 const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"; - var programs = new List(); - using (var root = Registry.LocalMachine.OpenSubKey(appPaths)) + IEnumerable programs = Enumerable.Empty(); + + using var rootMachine = Registry.LocalMachine.OpenSubKey(appPaths); + using var rootUser = Registry.CurrentUser.OpenSubKey(appPaths); + + if (rootMachine != null) { - if (root != null) - { - programs.AddRange(GetProgramsFromRegistry(root)); - } + programs = programs.Concat(GetProgramsFromRegistry(rootMachine)); } - using (var root = Registry.CurrentUser.OpenSubKey(appPaths)) + if (rootUser != null) { - if (root != null) - { - programs.AddRange(GetProgramsFromRegistry(root)); - } + programs = programs.Concat(GetProgramsFromRegistry(rootUser)); } + var disabledProgramsList = Main._settings.DisabledProgramSources; - var toFilter = programs.AsParallel().Where(p => suffixes.Contains(Extension(p.ExecutableName))); + var toFilter = programs.Where(p => suffixes.Contains(Extension(p.ExecutableName))); var filtered = toFilter.Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)).Select(t1 => t1); - return filtered; + return filtered.ToList(); } private static IEnumerable GetProgramsFromRegistry(RegistryKey root) @@ -418,14 +419,43 @@ namespace Flow.Launcher.Plugin.Program.Programs return entry; } + private class Win32ComparatorBasedonDescription : IEqualityComparer + { + public readonly static Win32ComparatorBasedonDescription Default = new Win32ComparatorBasedonDescription(); + + public bool Equals(Win32 x, Win32 y) + { + return x.Description == y.Description; + } + + public int GetHashCode(Win32 obj) + { + return obj.Description.GetHashCode(); + } + } + + private static Win32[] ProgramsHasher(IEnumerable programs) + => programs.GroupBy(p => p.FullPath.ToLower()) + .SelectMany(g => + { + var tempList = g.ToList(); + if (tempList.Count() > 1) + return g.Where(p => !string.IsNullOrEmpty(p.Description)) + .Distinct(Win32ComparatorBasedonDescription.Default); + else + return g.Take(1); + }).ToArray(); + + public static Win32[] All(Settings settings) { try { - var programs = new List().AsParallel(); + var programs = Enumerable.Empty(); var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes); programs = programs.Concat(unregistered); + if (settings.EnableRegistrySource) { var appPaths = AppPathsPrograms(settings.ProgramSuffixes); @@ -437,8 +467,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var startMenu = StartMenuPrograms(settings.ProgramSuffixes); programs = programs.Concat(startMenu); } - - return programs.ToArray(); + return ProgramsHasher(programs); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. catch (Exception e) From f693f1c6affba6f7ab466565b10ccde2eb07258c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 20 Dec 2020 17:25:14 +0800 Subject: [PATCH 08/56] use Array.empty instead of new [0] to avoid unused allocation --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 0f976cd58..e7a7c2bd7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -481,7 +481,7 @@ namespace Flow.Launcher.Plugin.Program.Programs { ProgramLogger.LogException("|Win32|All|Not available|An unexpected error occurred", e); - return new Win32[0]; + return Array.Empty(); } #endif } From d35d7ae2d6891bcb074fc58849a047513ff8cb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 23:45:15 +0800 Subject: [PATCH 09/56] Remove unused catch and optimize code --- .../Programs/Win32.cs | 119 ++++++++---------- 1 file changed, 54 insertions(+), 65 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index e7a7c2bd7..b6ca7ffcc 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -16,7 +16,6 @@ using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Plugin.Program.Programs { - [Serializable] public class Win32 : IProgram { @@ -127,19 +126,19 @@ namespace Flow.Launcher.Plugin.Program.Programs 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._settings.CustomizedArgs + .Replace("%s", $"\"{ParentDirectory}\"") + .Replace("%f", $"\"{FullPath}\"") + : Main._settings.CustomizedExplorer == Settings.Explorer + ? $"/select,\"{FullPath}\"" + : Settings.ExplorerArgs; Main.StartProcess(Process.Start, - new ProcessStartInfo( - !string.IsNullOrWhiteSpace(Main._settings.CustomizedExplorer) - ? Main._settings.CustomizedExplorer - : Settings.Explorer, - args)); + new ProcessStartInfo( + !string.IsNullOrWhiteSpace(Main._settings.CustomizedExplorer) + ? Main._settings.CustomizedExplorer + : Settings.Explorer, + args)); return true; }, @@ -150,7 +149,6 @@ namespace Flow.Launcher.Plugin.Program.Programs } - public override string ToString() { return ExecutableName; @@ -176,9 +174,9 @@ namespace Flow.Launcher.Plugin.Program.Programs catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { ProgramLogger.LogException($"|Win32|Win32Program|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + $"|Permission denied when trying to load the program from {path}", e); - return new Win32() { Valid = false, Enabled = false }; + return new Win32() {Valid = false, Enabled = false}; } } @@ -216,13 +214,15 @@ namespace Flow.Launcher.Plugin.Program.Programs } } } + return program; } catch (COMException e) { // C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\MiracastView.lnk always cause exception ProgramLogger.LogException($"|Win32|LnkProgram|{path}" + - "|Error caused likely due to trying to get the description of the program", e); + "|Error caused likely due to trying to get the description of the program", + e); program.Valid = false; return program; @@ -252,37 +252,22 @@ namespace Flow.Launcher.Plugin.Program.Programs catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { ProgramLogger.LogException($"|Win32|ExeProgram|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + $"|Permission denied when trying to load the program from {path}", e); - return new Win32() { Valid = false, Enabled = false }; + return new Win32() {Valid = false, Enabled = false}; } } private static IEnumerable ProgramPaths(string directory, string[] suffixes) { if (!Directory.Exists(directory)) - return new string[] { }; - try - { - var paths = Directory.EnumerateFiles(directory, "*", new EnumerationOptions - { - IgnoreInaccessible = true, - RecurseSubdirectories = true - }) - .Where(x => suffixes.Contains(Extension(x))); + return Enumerable.Empty(); - return paths; - } - catch (DirectoryNotFoundException e) + return Directory.EnumerateFiles(directory, "*", new EnumerationOptions { - 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[] { }; - } + IgnoreInaccessible = true, + RecurseSubdirectories = true + }).Where(x => suffixes.Contains(Extension(x))); } private static string Extension(string path) @@ -328,13 +313,13 @@ namespace Flow.Launcher.Plugin.Program.Programs var toFilter = paths1.Concat(paths2); var programs = toFilter - .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) - .Distinct() - .Select(x => Extension(x) switch - { - ShortcutExtension => LnkProgram(x), - _ => Win32Program(x) - }).Where(x => x.Valid); + .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; } @@ -351,6 +336,7 @@ namespace Flow.Launcher.Plugin.Program.Programs { programs = programs.Concat(GetProgramsFromRegistry(rootMachine)); } + if (rootUser != null) { programs = programs.Concat(GetProgramsFromRegistry(rootUser)); @@ -360,7 +346,9 @@ namespace Flow.Launcher.Plugin.Program.Programs var disabledProgramsList = Main._settings.DisabledProgramSources; var toFilter = programs.Where(p => suffixes.Contains(Extension(p.ExecutableName))); - var filtered = toFilter.Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)).Select(t1 => t1); + var filtered = toFilter + .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)) + .Select(t1 => t1); return filtered.ToList(); } @@ -368,10 +356,10 @@ namespace Flow.Launcher.Plugin.Program.Programs private static IEnumerable GetProgramsFromRegistry(RegistryKey root) { return root - .GetSubKeyNames() - .Select(x => GetProgramPathFromRegistrySubKeys(root, x)) - .Distinct() - .Select(x => GetProgramFromPath(x)); + .GetSubKeyNames() + .Select(x => GetProgramPathFromRegistrySubKeys(root, x)) + .Distinct() + .Select(GetProgramFromPath); } private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string subkey) @@ -397,7 +385,7 @@ namespace Flow.Launcher.Plugin.Program.Programs catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { ProgramLogger.LogException($"|Win32|GetProgramPathFromRegistrySubKeys|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + $"|Permission denied when trying to load the program from {path}", e); return string.Empty; } @@ -419,13 +407,13 @@ namespace Flow.Launcher.Plugin.Program.Programs return entry; } - private class Win32ComparatorBasedonDescription : IEqualityComparer + private class Win32ComparatorWithDescription : IEqualityComparer { - public readonly static Win32ComparatorBasedonDescription Default = new Win32ComparatorBasedonDescription(); + public static readonly Win32ComparatorWithDescription Default = new Win32ComparatorWithDescription(); public bool Equals(Win32 x, Win32 y) { - return x.Description == y.Description; + return x?.Description == y?.Description; } public int GetHashCode(Win32 obj) @@ -435,16 +423,16 @@ namespace Flow.Launcher.Plugin.Program.Programs } private static Win32[] ProgramsHasher(IEnumerable programs) - => programs.GroupBy(p => p.FullPath.ToLower()) - .SelectMany(g => - { - var tempList = g.ToList(); - if (tempList.Count() > 1) - return g.Where(p => !string.IsNullOrEmpty(p.Description)) - .Distinct(Win32ComparatorBasedonDescription.Default); - else - return g.Take(1); - }).ToArray(); + { + return programs.GroupBy(p => p.FullPath.ToLower()) + .SelectMany(g => + { + if (g.Count() > 1) + return g.Where(p => !string.IsNullOrEmpty(p.Description)) + .Distinct(Win32ComparatorWithDescription.Default); + return g; + }).ToArray(); + } public static Win32[] All(Settings settings) @@ -467,6 +455,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var startMenu = StartMenuPrograms(settings.ProgramSuffixes); programs = programs.Concat(startMenu); } + return ProgramsHasher(programs); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. @@ -486,4 +475,4 @@ namespace Flow.Launcher.Plugin.Program.Programs #endif } } -} +} \ No newline at end of file From 5d894ec154dca7949cb0d9cb8c67c22d9e667519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 25 Jan 2021 17:18:48 +0800 Subject: [PATCH 10/56] Don't await download manifest when it is unavaliable, but only await when calling install method to allow user using uninstall when manifest is not ready. --- .../Flow.Launcher.Plugin.PluginsManager/Main.cs | 15 +++++++++++++-- .../PluginsManager.cs | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 66bfd2ab5..7836f1441 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -69,8 +69,19 @@ namespace Flow.Launcher.Plugin.PluginsManager if ((DateTime.Now - lastUpdateTime).TotalHours > 12) // 12 hours { - await pluginManager.UpdateManifest(); - lastUpdateTime = DateTime.Now; + _ = pluginManager.UpdateManifest().ContinueWith(t => + { + if (t.IsCompletedSuccessfully) + { + lastUpdateTime = DateTime.Now; + } + else + { + Context.API.ShowMsg("Plugin Manifest Download Fail.", + "Please check if you can connect to github.com. " + + "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); + } + }); } return search switch diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 0f5e6d9e8..e25d97b36 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -46,9 +46,9 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = settings; } - internal async Task UpdateManifest() + internal Task UpdateManifest() { - await pluginsManifest.DownloadManifest(); + return _downloadManifestTask = pluginsManifest.DownloadManifest(); } internal List GetDefaultHotKeys() From 89453f441d6de762802ecb7e2eff729b2aae97b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 29 Jan 2021 11:37:26 +0800 Subject: [PATCH 11/56] Await Update when manifest is not ready --- .../Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- .../PluginsManager.cs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 7836f1441..c9f9ca2a7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -88,7 +88,7 @@ namespace Flow.Launcher.Plugin.PluginsManager { var s when s.StartsWith(Settings.HotKeyInstall) => await pluginManager.RequestInstallOrUpdate(s, token), var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s), - var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s), + var s when s.StartsWith(Settings.HotkeyUpdate) => await pluginManager.RequestUpdate(s, token), _ => pluginManager.GetDefaultHotKeys().Where(hotkey => { hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score; diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index e25d97b36..eb0caec3f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -149,8 +149,19 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.RestartApp(); } - internal List RequestUpdate(string search) + internal async ValueTask> RequestUpdate(string search, CancellationToken token) { + if (!pluginsManifest.UserPlugins.Any() && + _downloadManifestTask.Status != TaskStatus.Running) + { + _downloadManifestTask = pluginsManifest.DownloadManifest(); + } + + await _downloadManifestTask; + + token.ThrowIfCancellationRequested(); + + var autocompletedResults = AutoCompleteReturnAllResults(search, Settings.HotkeyUpdate, "Update", @@ -286,8 +297,7 @@ namespace Flow.Launcher.Plugin.PluginsManager await _downloadManifestTask; - if (token.IsCancellationRequested) - return null; + token.ThrowIfCancellationRequested(); var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); From 2271ec16fd556e88ccf21f1765e51ac27848ee18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 29 Jan 2021 12:17:17 +0800 Subject: [PATCH 12/56] Optimize exception message logic --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 13 ++----------- .../PluginsManager.cs | 6 +++++- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index c9f9ca2a7..dbd1600c9 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -71,17 +71,8 @@ namespace Flow.Launcher.Plugin.PluginsManager { _ = pluginManager.UpdateManifest().ContinueWith(t => { - if (t.IsCompletedSuccessfully) - { - lastUpdateTime = DateTime.Now; - } - else - { - Context.API.ShowMsg("Plugin Manifest Download Fail.", - "Please check if you can connect to github.com. " + - "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); - } - }); + lastUpdateTime = DateTime.Now; + }, TaskContinuationOptions.OnlyOnRanToCompletion); } return search switch diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index eb0caec3f..090e93bf2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -48,7 +48,11 @@ namespace Flow.Launcher.Plugin.PluginsManager internal Task UpdateManifest() { - return _downloadManifestTask = pluginsManifest.DownloadManifest(); + return _downloadManifestTask = pluginsManifest.DownloadManifest().ContinueWith(t => + Context.API.ShowMsg("Plugin Manifest Download Fail.", + "Please check if you can connect to github.com. " + + "This error means you may not be able to Install and Update Plugin.", icoPath, false), + TaskContinuationOptions.OnlyOnFaulted); } internal List GetDefaultHotKeys() From 4933a2e28c4d1572172e49ae0a23288ad9219bdd Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 31 Jan 2021 17:49:08 +1100 Subject: [PATCH 13/56] add python installation --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 1 + Flow.Launcher.Core/Plugin/PluginsLoader.cs | 113 ++++++++++++++----- 2 files changed, 88 insertions(+), 26 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 189a6669e..deb73ba80 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -53,6 +53,7 @@ + diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index fcf178445..45be6c7c1 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -3,13 +3,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Loader; using System.Threading.Tasks; using System.Windows.Forms; +using Droplex; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Core.Plugin { @@ -22,7 +23,7 @@ namespace Flow.Launcher.Core.Plugin public static List Plugins(List metadatas, PluginsSettings settings) { var dotnetPlugins = DotNetPlugins(metadatas); - var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory); + var pythonPlugins = PythonPlugins(metadatas, settings); var executablePlugins = ExecutablePlugins(metadatas); var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList(); return plugins; @@ -113,11 +114,14 @@ namespace Flow.Launcher.Core.Plugin return plugins; } - public static IEnumerable PythonPlugins(List source, string pythonDirectory) + public static IEnumerable PythonPlugins(List source, PluginsSettings settings) { - // try to set Constant.PythonPath, either from + if (!source.Any(o => o.Language.ToUpper() == AllowedLanguage.Python)) + return new List(); + + // Try setting Constant.PythonPath first, either from // PATH or from the given pythonDirectory - if (string.IsNullOrEmpty(pythonDirectory)) + if (string.IsNullOrEmpty(settings.PythonDirectory)) { var paths = Environment.GetEnvironmentVariable(PATH); if (paths != null) @@ -129,47 +133,104 @@ namespace Flow.Launcher.Core.Plugin if (pythonInPath) { - Constant.PythonPath = PythonExecutable; + Constant.PythonPath = + Path.Combine(paths.Split(';').Where(p => p.ToLower().Contains(Python)).FirstOrDefault(), PythonExecutable); + settings.PythonDirectory = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, Constant.PythonPath); } else { - Log.Error("|PluginsLoader.PythonPlugins|Python can't be found in PATH."); + Log.Error("PluginsLoader","Failed to set Python path despite the environment variable PATH is found", "PythonPlugins"); } } - else - { - Log.Error("|PluginsLoader.PythonPlugins|PATH environment variable is not set."); - } } else { - var path = Path.Combine(pythonDirectory, PythonExecutable); + var path = Path.Combine(settings.PythonDirectory, PythonExecutable); if (File.Exists(path)) { Constant.PythonPath = path; } else { - Log.Error($"|PluginsLoader.PythonPlugins|Can't find python executable in {path}"); + Log.Error("PluginsLoader",$"Tried to automatically set from Settings.PythonDirectory " + + $"but can't find python executable in {path}", "PythonPlugins"); } } - // if we have a path to the python executable, - // load every python plugin pair. - if (String.IsNullOrEmpty(Constant.PythonPath)) + if (string.IsNullOrEmpty(settings.PythonDirectory)) { + if (MessageBox.Show("Flow detected you have installed Python plugins, " + + "would you like to install Python to run them? " + + Environment.NewLine + Environment.NewLine + + "Click no if it's already installed, " + + "and you will be prompted to select the folder that contains the Python executable", + string.Empty, MessageBoxButtons.YesNo) == DialogResult.No + && string.IsNullOrEmpty(settings.PythonDirectory)) + { + var dlg = new FolderBrowserDialog + { + SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + }; + + var result = dlg.ShowDialog(); + if (result == DialogResult.OK) + { + string pythonDirectory = dlg.SelectedPath; + if (!string.IsNullOrEmpty(pythonDirectory)) + { + var pythonPath = Path.Combine(pythonDirectory, PythonExecutable); + if (File.Exists(pythonPath)) + { + settings.PythonDirectory = pythonDirectory; + Constant.PythonPath = pythonPath; + } + else + { + MessageBox.Show("Can't find python in given directory"); + } + } + } + } + else + { + var installation = Task.Run(async () => { await DroplexPackage.Drop(App.python3_9_1).ConfigureAwait(false); }); + installation.Wait(); + + var installedPythonDirectory = + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39"); + var pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable); + if (FilesFolders.FileExists(pythonPath)) + { + settings.PythonDirectory = installedPythonDirectory; + Constant.PythonPath = pythonPath; + } + else + { + Log.Error("PluginsLoader", + $"Failed to set Python path after Droplex install, {pythonPath} does not exist", + "PythonPlugins"); + } + } + } + + if (string.IsNullOrEmpty(settings.PythonDirectory)) + { + MessageBox.Show("Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom)."); + Log.Error("PluginsLoader", + $"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.", + "PythonPlugins"); + return new List(); } - else - { - return source - .Where(o => o.Language.ToUpper() == AllowedLanguage.Python) - .Select(metadata => new PluginPair - { - Plugin = new PythonPlugin(Constant.PythonPath), - Metadata = metadata - }); - } + + return source + .Where(o => o.Language.ToUpper() == AllowedLanguage.Python) + .Select(metadata => new PluginPair + { + Plugin = new PythonPlugin(Constant.PythonPath), + Metadata = metadata + }) + .ToList(); } public static IEnumerable ExecutablePlugins(IEnumerable source) From a6e56025207ddf64087d93a6769cd025b8ab1d6e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 3 Feb 2021 13:00:16 +1100 Subject: [PATCH 14/56] formatting fix --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index dbd1600c9..46a57757b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -48,7 +48,7 @@ namespace Flow.Launcher.Plugin.PluginsManager { context.API.ShowMsg("Plugin Manifest Download Fail.", "Please check if you can connect to github.com. " + - "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); + "This error means you may not be able to install or update plugins.", pluginManager.icoPath, false); } }); diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 090e93bf2..9606db7c1 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -165,7 +165,6 @@ namespace Flow.Launcher.Plugin.PluginsManager token.ThrowIfCancellationRequested(); - var autocompletedResults = AutoCompleteReturnAllResults(search, Settings.HotkeyUpdate, "Update", From 71664bddc0b42f3786031076f2de12f5afdaba5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 3 Feb 2021 10:23:38 +0800 Subject: [PATCH 15/56] Optimize Logic --- .../Main.cs | 18 ++++----------- .../PluginsManager.cs | 23 ++++++++++--------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index dbd1600c9..3707beee2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -37,20 +37,10 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = viewModel.Settings; contextMenu = new ContextMenu(Context); pluginManager = new PluginsManager(Context, Settings); - var updateManifestTask = pluginManager.UpdateManifest(); - _ = updateManifestTask.ContinueWith(t => - { - if (t.IsCompletedSuccessfully) - { - lastUpdateTime = DateTime.Now; - } - else - { - context.API.ShowMsg("Plugin Manifest Download Fail.", - "Please check if you can connect to github.com. " + - "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); - } - }); + _ = pluginManager.UpdateManifest().ContinueWith(_ => + { + lastUpdateTime = DateTime.Now; + }, TaskContinuationOptions.OnlyOnRanToCompletion); return Task.CompletedTask; } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 090e93bf2..f14cd1be4 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -48,11 +48,18 @@ namespace Flow.Launcher.Plugin.PluginsManager internal Task UpdateManifest() { - return _downloadManifestTask = pluginsManifest.DownloadManifest().ContinueWith(t => + if (_downloadManifestTask.Status == TaskStatus.Running) + { + return _downloadManifestTask; + } + else + { + return _downloadManifestTask = pluginsManifest.DownloadManifest().ContinueWith(t => Context.API.ShowMsg("Plugin Manifest Download Fail.", "Please check if you can connect to github.com. " + "This error means you may not be able to Install and Update Plugin.", icoPath, false), TaskContinuationOptions.OnlyOnFaulted); + } } internal List GetDefaultHotKeys() @@ -155,14 +162,11 @@ namespace Flow.Launcher.Plugin.PluginsManager internal async ValueTask> RequestUpdate(string search, CancellationToken token) { - if (!pluginsManifest.UserPlugins.Any() && - _downloadManifestTask.Status != TaskStatus.Running) + if (!pluginsManifest.UserPlugins.Any()) { - _downloadManifestTask = pluginsManifest.DownloadManifest(); + await UpdateManifest(); } - await _downloadManifestTask; - token.ThrowIfCancellationRequested(); @@ -293,14 +297,11 @@ namespace Flow.Launcher.Plugin.PluginsManager internal async ValueTask> RequestInstallOrUpdate(string searchName, CancellationToken token) { - if (!pluginsManifest.UserPlugins.Any() && - _downloadManifestTask.Status != TaskStatus.Running) + if (!pluginsManifest.UserPlugins.Any()) { - _downloadManifestTask = pluginsManifest.DownloadManifest(); + await UpdateManifest(); } - await _downloadManifestTask; - token.ThrowIfCancellationRequested(); var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); From 1f6e6e5e09590b071cd94757c745e0c88a889431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 3 Feb 2021 10:25:29 +0800 Subject: [PATCH 16/56] fix format --- .../Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 5ea38b7c3..184e5c230 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -46,6 +46,9 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = settings; } + private Task _downloadManifestTask = Task.CompletedTask; + + internal Task UpdateManifest() { if (_downloadManifestTask.Status == TaskStatus.Running) @@ -292,8 +295,6 @@ namespace Flow.Launcher.Plugin.PluginsManager .ToList(); } - private Task _downloadManifestTask = Task.CompletedTask; - internal async ValueTask> RequestInstallOrUpdate(string searchName, CancellationToken token) { if (!pluginsManifest.UserPlugins.Any()) From f3d0b8861641c70444cc85b26302336a37e12da1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 5 Feb 2021 08:08:33 +1100 Subject: [PATCH 17/56] remove task --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 45be6c7c1..2c75f9633 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -193,8 +193,7 @@ namespace Flow.Launcher.Core.Plugin } else { - var installation = Task.Run(async () => { await DroplexPackage.Drop(App.python3_9_1).ConfigureAwait(false); }); - installation.Wait(); + DroplexPackage.Drop(App.python3_9_1).Wait(); var installedPythonDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39"); From 1af95254874874029d818b7ffe3d3a0e2e26da47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 6 Feb 2021 16:12:38 +0800 Subject: [PATCH 18/56] move ISavable to Flow.Launcher.Plugin\Interfaces --- .../Storage => Flow.Launcher.Plugin/Interfaces}/ISavable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {Flow.Launcher.Infrastructure/Storage => Flow.Launcher.Plugin/Interfaces}/ISavable.cs (81%) diff --git a/Flow.Launcher.Infrastructure/Storage/ISavable.cs b/Flow.Launcher.Plugin/Interfaces/ISavable.cs similarity index 81% rename from Flow.Launcher.Infrastructure/Storage/ISavable.cs rename to Flow.Launcher.Plugin/Interfaces/ISavable.cs index 8294a3df8..7c1110e0e 100644 --- a/Flow.Launcher.Infrastructure/Storage/ISavable.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISavable.cs @@ -1,4 +1,4 @@ -namespace Flow.Launcher.Infrastructure.Storage +namespace Flow.Launcher.Plugin { /// /// Save plugin settings/cache, From 2b9823a7acfead7dbea79dbf8435ec187ec04720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Feb 2021 12:49:27 +0800 Subject: [PATCH 19/56] Use Customized Except for removing disabled source --- .../Programs/Win32.cs | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index b6ca7ffcc..48b0c4c32 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -285,9 +285,8 @@ namespace Flow.Launcher.Plugin.Program.Programs private static IEnumerable UnregisteredPrograms(List sources, string[] suffixes) { - var paths = sources.Where(s => Directory.Exists(s.Location) && s.Enabled) - .SelectMany(s => ProgramPaths(s.Location, suffixes)) - .Where(t1 => !Main._settings.DisabledProgramSources.Any(x => t1 == x.UniqueIdentifier)) + var paths = ExceptDisabledSource(sources.Where(s => Directory.Exists(s.Location) && s.Enabled) + .SelectMany(s => ProgramPaths(s.Location, suffixes)), x => x) .Distinct(); var programs = paths.Select(x => Extension(x) switch @@ -312,9 +311,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var toFilter = paths1.Concat(paths2); - var programs = toFilter - .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) - .Distinct() + var programs = ExceptDisabledSource(toFilter.Distinct()) .Select(x => Extension(x) switch { ShortcutExtension => LnkProgram(x), @@ -327,39 +324,36 @@ namespace Flow.Launcher.Plugin.Program.Programs { // https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121 const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"; - IEnumerable programs = Enumerable.Empty(); + + IEnumerable toFilter = Enumerable.Empty(); using var rootMachine = Registry.LocalMachine.OpenSubKey(appPaths); using var rootUser = Registry.CurrentUser.OpenSubKey(appPaths); if (rootMachine != null) { - programs = programs.Concat(GetProgramsFromRegistry(rootMachine)); + toFilter = toFilter.Concat(GetPathFromRegistry(rootMachine)); } if (rootUser != null) { - programs = programs.Concat(GetProgramsFromRegistry(rootUser)); + toFilter = toFilter.Concat(GetPathFromRegistry(rootUser)); } - var disabledProgramsList = Main._settings.DisabledProgramSources; - var toFilter = programs.Where(p => suffixes.Contains(Extension(p.ExecutableName))); + toFilter = toFilter.Distinct().Where(p => suffixes.Contains(Extension(p))); - var filtered = toFilter - .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)) - .Select(t1 => t1); + var filtered = ExceptDisabledSource(toFilter); - return filtered.ToList(); + return filtered.Select(GetProgramFromPath).ToList(); // ToList due to disposing issue } - private static IEnumerable GetProgramsFromRegistry(RegistryKey root) + private static IEnumerable GetPathFromRegistry(RegistryKey root) { return root .GetSubKeyNames() .Select(x => GetProgramPathFromRegistrySubKeys(root, x)) - .Distinct() - .Select(GetProgramFromPath); + .Distinct(); } private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string subkey) @@ -394,12 +388,12 @@ namespace Flow.Launcher.Plugin.Program.Programs private static Win32 GetProgramFromPath(string path) { if (string.IsNullOrEmpty(path)) - return new Win32(); + return null; path = Environment.ExpandEnvironmentVariables(path); if (!File.Exists(path)) - return new Win32(); + return null; var entry = Win32Program(path); entry.ExecutableName = Path.GetFileName(path); @@ -407,19 +401,35 @@ namespace Flow.Launcher.Plugin.Program.Programs return entry; } + public static IEnumerable ExceptDisabledSource(IEnumerable sources) => ExceptDisabledSource(sources, x => x); + + public static IEnumerable ExceptDisabledSource(IEnumerable sources, + Func keySelector) + { + return Main._settings.DisabledProgramSources.Count == 0 + ? sources + : ExceptDisabledSourceEnumerable(sources, keySelector); + + static IEnumerable ExceptDisabledSourceEnumerable(IEnumerable elements, + Func selector) + { + var set = Main._settings.DisabledProgramSources.Select(x => x.UniqueIdentifier).ToHashSet(); + + foreach (var element in elements) + { + if (!set.Contains(selector(element))) + yield return element; + } + } + } + private class Win32ComparatorWithDescription : IEqualityComparer { public static readonly Win32ComparatorWithDescription Default = new Win32ComparatorWithDescription(); - public bool Equals(Win32 x, Win32 y) - { - return x?.Description == y?.Description; - } + public bool Equals(Win32 x, Win32 y) => x?.Description == y?.Description; - public int GetHashCode(Win32 obj) - { - return obj.Description.GetHashCode(); - } + public int GetHashCode(Win32 obj) => obj.Description.GetHashCode(); } private static Win32[] ProgramsHasher(IEnumerable programs) @@ -456,7 +466,8 @@ namespace Flow.Launcher.Plugin.Program.Programs programs = programs.Concat(startMenu); } - return ProgramsHasher(programs); + + return ProgramsHasher(programs.Where(p => p != null)); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. catch (Exception e) From 70db600c9fb3d104dcf4eb7f2bb2c256a677cc24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Feb 2021 12:56:40 +0800 Subject: [PATCH 20/56] optimize format --- .../Programs/Win32.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 48b0c4c32..c1de4b5da 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -401,7 +401,10 @@ namespace Flow.Launcher.Plugin.Program.Programs return entry; } - public static IEnumerable ExceptDisabledSource(IEnumerable sources) => ExceptDisabledSource(sources, x => x); + public static IEnumerable ExceptDisabledSource(IEnumerable sources) + { + return ExceptDisabledSource(sources, x => x); + } public static IEnumerable ExceptDisabledSource(IEnumerable sources, Func keySelector) @@ -427,9 +430,15 @@ namespace Flow.Launcher.Plugin.Program.Programs { public static readonly Win32ComparatorWithDescription Default = new Win32ComparatorWithDescription(); - public bool Equals(Win32 x, Win32 y) => x?.Description == y?.Description; + public bool Equals(Win32 x, Win32 y) + { + return x?.Description == y?.Description; + } - public int GetHashCode(Win32 obj) => obj.Description.GetHashCode(); + public int GetHashCode(Win32 obj) + { + return obj.Description.GetHashCode(); + } } private static Win32[] ProgramsHasher(IEnumerable programs) From 276395e5f47165950aa609315afb33a251e76062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 9 Feb 2021 15:59:55 +0800 Subject: [PATCH 21/56] Use customized distinctBy instead of customized equalitycomparator --- .../Programs/Win32.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f2adbf243..d73483818 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -446,18 +446,13 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - private class Win32ComparatorWithDescription : IEqualityComparer + public static IEnumerable DistinctBy(IEnumerable source, Func selector) { - public static readonly Win32ComparatorWithDescription Default = new Win32ComparatorWithDescription(); - - public bool Equals(Win32 x, Win32 y) + var set = new HashSet(); + foreach (var item in source) { - return x?.Description == y?.Description; - } - - public int GetHashCode(Win32 obj) - { - return obj.Description.GetHashCode(); + if (set.Add(selector(item))) + yield return item; } } @@ -467,8 +462,7 @@ namespace Flow.Launcher.Plugin.Program.Programs .SelectMany(g => { if (g.Count() > 1) - return g.Where(p => !string.IsNullOrEmpty(p.Description)) - .Distinct(Win32ComparatorWithDescription.Default); + return DistinctBy(g.Where(p => !string.IsNullOrEmpty(p.Description)), x => x.Description); return g; }).ToArray(); } From 20f895c7810a3559800b3f5b7a3f9cf5e59215de Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 10 Feb 2021 21:17:03 +1100 Subject: [PATCH 22/56] upgrade Droplex to use mirror site --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index deb73ba80..b0cc07e1a 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -53,7 +53,7 @@ - + From 416ad3bb89bc862eae865da0090714d98e8a4f82 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 11 Feb 2021 14:00:17 +1100 Subject: [PATCH 23/56] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9af47a5ee..39e8a7dea 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,11 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up. **Integrations** - - If you use Python plugins: - - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. + - Python plugins: + - Once a Python plugin has been detected at start up, you will be prompted to either select the location or allow Python 3.9.1 to automatic download and install. - Add Python to `%PATH%` or set it in flow's settings. - - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`. + - Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`. + - The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`. - Start to launch your Python plugins. - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). From 80f1585f88e1f17492d5c9896e8f91c32e610978 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 12 Feb 2021 14:45:31 +1100 Subject: [PATCH 24/56] add press F5 to reload all plugin data --- Flow.Launcher/Languages/en.xaml | 1 + Flow.Launcher/MainWindow.xaml | 1 + Flow.Launcher/Msg.xaml.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 29 +++++++++++++++++++----- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index b6bf76b7f..a036949fc 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -121,6 +121,7 @@ New Action Keyword can't be empty This new Action Keyword is already assigned to another plugin, please choose a different one Success + Completed successfully Use * if you don't want to specify an action keyword diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 4cc0b4428..aa0240dd4 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -34,6 +34,7 @@ + diff --git a/Flow.Launcher/Msg.xaml.cs b/Flow.Launcher/Msg.xaml.cs index 4129ce28b..6bb2fc2dc 100644 --- a/Flow.Launcher/Msg.xaml.cs +++ b/Flow.Launcher/Msg.xaml.cs @@ -66,7 +66,7 @@ namespace Flow.Launcher } if (!File.Exists(iconPath)) { - imgIco.Source = ImageLoader.Load(Path.Combine(Infrastructure.Constant.ProgramDirectory, "Images\\app.png")); + imgIco.Source = ImageLoader.Load(Path.Combine(Constant.ProgramDirectory, "Images\\app.png")); } else { imgIco.Source = ImageLoader.Load(iconPath); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index afbe6e197..a12912b67 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -89,7 +89,6 @@ namespace Flow.Launcher.ViewModel _resultsViewUpdateTask = Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); - async Task updateAction() { var queue = new Dictionary(); @@ -105,9 +104,7 @@ namespace Flow.Launcher.ViewModel UpdateResultView(queue.Values); } - } - - ; + }; void continueAction(Task t) { @@ -115,8 +112,8 @@ namespace Flow.Launcher.ViewModel throw t.Exception; #else Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}"); - _resultsViewUpdateTask = - Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); + _resultsViewUpdateTask = + Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); #endif } } @@ -225,6 +222,25 @@ namespace Flow.Launcher.ViewModel SelectedResults = Results; } }); + + ReloadPluginDataCommand = new RelayCommand(_ => + { + var msg = new Msg { Owner = Application.Current.MainWindow }; + + MainWindowVisibility = Visibility.Collapsed; + + PluginManager + .ReloadData() + .ContinueWith(_ => + Application.Current.Dispatcher.Invoke(() => + { + msg.Show( + InternationalizationManager.Instance.GetTranslation("success"), + InternationalizationManager.Instance.GetTranslation("completedSuccessfully"), + ""); + })) + .ConfigureAwait(false); + }); } #endregion @@ -313,6 +329,7 @@ namespace Flow.Launcher.ViewModel public ICommand LoadContextMenuCommand { get; set; } public ICommand LoadHistoryCommand { get; set; } public ICommand OpenResultCommand { get; set; } + public ICommand ReloadPluginDataCommand { get; set; } public string OpenResultCommandModifiers { get; private set; } From fcc0bb11facfa21e795b2f7fac10a93bbf6c4949 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 12 Feb 2021 14:49:21 +1100 Subject: [PATCH 25/56] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9af47a5ee..3e420bfac 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Windows may complain about security due to code not being signed, this will be c - Open context menu: on the selected result, press Ctrl+O/Shift+Enter. - Cancel/Return to previous screen: Esc. - Install/Uninstall/Update plugins: in the search window, type `pm install`/`pm uninstall`/`pm update` + the plugin name. +- Press `F5` while in the query window to reload all plugin data. - Saved user settings are located: - If using roaming: `%APPDATA%\FlowLauncher` - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` From b503584dbf8be2782085ade7610c423d98516143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 12 Feb 2021 22:03:35 +0800 Subject: [PATCH 26/56] Use app.ico as icon instead of app.png for setting window --- Flow.Launcher/Flow.Launcher.csproj | 9 +++++++++ Flow.Launcher/Images/app.ico | Bin 0 -> 116895 bytes Flow.Launcher/SettingWindow.xaml | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 Flow.Launcher/Images/app.ico diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 289a502d0..ce3bebd57 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -66,6 +66,9 @@ PreserveNewest + + PreserveNewest + @@ -94,6 +97,12 @@ + + + PreserveNewest + + + diff --git a/Flow.Launcher/Images/app.ico b/Flow.Launcher/Images/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..36b1d22d0b3bcfb91c8ff451953e9eb421da1385 GIT binary patch literal 116895 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 literal 0 HcmV?d00001 diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 4c7eac114..8d88ff3fd 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -11,13 +11,14 @@ xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" x:Class="Flow.Launcher.SettingWindow" mc:Ignorable="d" - Icon="Images\app.png" + Icon="Images\app.ico" Title="{DynamicResource flowlauncher_settings}" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Height="600" Width="800" Closed="OnClosed" d:DataContext="{d:DesignInstance vm:SettingWindowViewModel}"> + From 9df72010370ce04a12dcb4e60155474010211d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 12 Feb 2021 23:47:21 +0800 Subject: [PATCH 27/56] Remove setting after uninstalling plugin --- .../Flow.Launcher.Plugin.PluginsManager.csproj | 1 + .../Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj index cb2507a2b..f94027bcc 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj @@ -18,6 +18,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 881872fc1..b2f18e39a 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -1,8 +1,10 @@ using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.PluginsManager.Models; +using Flow.Launcher.Plugin.PluginsManager.ViewModels; using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; @@ -400,6 +402,12 @@ namespace Flow.Launcher.Plugin.PluginsManager private void Uninstall(PluginMetadata plugin) { + + + Core.Plugin.PluginManager.Settings.Plugins.Remove(plugin.ID); + Core.Plugin.PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); + + // Marked for deletion. Will be deleted on next start up using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); } From 59a767fafbaf469978ce548b2b62d830489e2495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 13 Feb 2021 12:22:59 +0800 Subject: [PATCH 28/56] remove unexpected space --- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index b2f18e39a..4949c4e04 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -402,8 +402,6 @@ namespace Flow.Launcher.Plugin.PluginsManager private void Uninstall(PluginMetadata plugin) { - - Core.Plugin.PluginManager.Settings.Plugins.Remove(plugin.ID); Core.Plugin.PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); From baf75c4985745a53b89640724635fa027e99ae39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 13 Feb 2021 12:25:02 +0800 Subject: [PATCH 29/56] remove extra copy --- Flow.Launcher/Flow.Launcher.csproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index ce3bebd57..01098771d 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -97,12 +97,6 @@ - - - PreserveNewest - - - From 0707ae39978019671683f7bd8650d9e0677d10f9 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 13 Feb 2021 21:03:26 +1100 Subject: [PATCH 30/56] fix formattng --- .../Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 4949c4e04..90d103bc7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -1,10 +1,9 @@ +using Flow.Launcher.Core.Plugin; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.PluginsManager.Models; -using Flow.Launcher.Plugin.PluginsManager.ViewModels; using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; @@ -402,9 +401,8 @@ namespace Flow.Launcher.Plugin.PluginsManager private void Uninstall(PluginMetadata plugin) { - Core.Plugin.PluginManager.Settings.Plugins.Remove(plugin.ID); - Core.Plugin.PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); - + PluginManager.Settings.Plugins.Remove(plugin.ID); + PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); // Marked for deletion. Will be deleted on next start up using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); From 92f757c47ed44c8fc852aaf1198cd70bdfed66b5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 13 Feb 2021 21:35:08 +1100 Subject: [PATCH 31/56] add restart notes --- Flow.Launcher/PublicAPIInstance.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 427fd9fc6..0afa67d1a 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -61,6 +61,9 @@ namespace Flow.Launcher // which will cause ungraceful exit SaveAppAllSettings(); + // Restart requires Squirrel's Update.exe to be present in the parent folder, + // it is only published from the project's release pipeline. When debugging without it, + // the project may not restart or just terminates. This is expected. UpdateManager.RestartApp(Constant.ApplicationFileName); } From 7c2c78733ef1dffcabf84aab39d813c54c259a99 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 13 Feb 2021 21:41:56 +1100 Subject: [PATCH 32/56] version bump PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index ad4601586..4cba7ec83 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.6.3", + "Version": "1.6.4", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From 13f737740e048bbea16d2d453f277ed7564eefd2 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 13 Feb 2021 22:01:52 +1100 Subject: [PATCH 33/56] fix formatting --- Flow.Launcher/Flow.Launcher.csproj | 6 +++--- Flow.Launcher/SettingWindow.xaml | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 01098771d..20e847f82 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -66,9 +66,9 @@ PreserveNewest - - PreserveNewest - + + PreserveNewest + diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 8d88ff3fd..cb0ef2def 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -18,7 +18,6 @@ Height="600" Width="800" Closed="OnClosed" d:DataContext="{d:DesignInstance vm:SettingWindowViewModel}"> - From f9b8216e3b9e9021e5ea0eaee39bea0eae3837d1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 14 Feb 2021 19:00:00 +1100 Subject: [PATCH 34/56] add copy bookmark's url to clipboard --- .../Images/copylink.png | Bin 0 -> 10725 bytes .../Languages/en.xaml | 2 + .../Main.cs | 47 ++++++++++++++++-- .../plugin.json | 2 +- 4 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.BrowserBookmark/Images/copylink.png diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Images/copylink.png b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Images/copylink.png new file mode 100644 index 0000000000000000000000000000000000000000..0dee870b615bc9caddeff44be4096b4b5f6fa92e GIT binary patch literal 10725 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 tab Set browser from path: Choose + Copy url + Copy the bookmark's url to clipboard \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 47493654f..3726f1683 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using System.Windows; using System.Windows.Controls; +using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.BrowserBookmark.Commands; using Flow.Launcher.Plugin.BrowserBookmark.Models; @@ -9,7 +12,7 @@ using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.BrowserBookmark { - public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable + public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable, IContextMenu { private PluginInitContext context; @@ -60,7 +63,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark } return true; - } + }, + ContextData = new BookmarkAttributes { Url = c.Url } }).Where(r => r.Score > 0); return returnList.ToList(); } @@ -84,7 +88,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark } return true; - } + }, + ContextData = new BookmarkAttributes { Url = c.Url } }).ToList(); } } @@ -115,5 +120,39 @@ namespace Flow.Launcher.Plugin.BrowserBookmark { _storage.Save(); } + + public List LoadContextMenus(Result selectedResult) + { + return new List() { + new Result + { + Title = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_title"), + SubTitle = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_subtitle"), + Action = _ => + { + try + { + Clipboard.SetText(((BookmarkAttributes)selectedResult.ContextData).Url); + + return true; + } + catch (Exception e) + { + var message = "Failed to set url in clipboard"; + Log.Exception("Main",message, e, "LoadContextMenus"); + + context.API.ShowMsg(message); + + return false; + } + }, + IcoPath = "Images\\copylink.png" + }}; + } + + internal class BookmarkAttributes + { + internal string Url { get; set; } + } } } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json index b0c3d2e29..62311f514 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json @@ -4,7 +4,7 @@ "Name": "Browser Bookmarks", "Description": "Search your browser bookmarks", "Author": "qianlifeng, Ioannis G.", - "Version": "1.3.2", + "Version": "1.4.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll", From 93f4b6d90adaf017a7e0df5a2dd6e8172a2686ef Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 14 Feb 2021 20:32:20 +1100 Subject: [PATCH 35/56] update to use SetDataObject --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 3726f1683..b889bb0d0 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -132,7 +132,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark { try { - Clipboard.SetText(((BookmarkAttributes)selectedResult.ContextData).Url); + Clipboard.SetDataObject(((BookmarkAttributes)selectedResult.ContextData).Url); return true; } From f8557da336171ddb738715d700e70dca4bf2a5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 18:08:30 +0800 Subject: [PATCH 36/56] add Logs, LoadJsonStorage, SaveJsonStorage to IPublicAPI --- .../Storage/JsonStorage.cs | 2 +- .../Storage/PluginJsonStorage.cs | 5 +++ Flow.Launcher.Plugin/IPublicAPI.cs | 12 ++++++ Flow.Launcher/PublicAPIInstance.cs | 39 +++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index f0e4a79fc..37cfec252 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Infrastructure.Storage public class JsonStrorage where T : new() { private readonly JsonSerializerOptions _serializerSettings; - private T _data; + protected T _data; // need a new directory name public const string DirectoryName = "Settings"; public const string FileSuffix = ".json"; diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index 5418e1837..ca7c454c4 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -15,5 +15,10 @@ namespace Flow.Launcher.Infrastructure.Storage FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); } + + public PluginJsonStorage(T data) : this() + { + _data = data; + } } } diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index dd73eb0e5..2a69a2495 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using System; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -100,5 +101,16 @@ namespace Flow.Launcher.Plugin void RemoveActionKeyword(string pluginId, string oldActionKeyword); + void LogDebug(string className, string message, [CallerMemberName] string methodName = ""); + + void LogInfo(string className, string message, [CallerMemberName] string methodName = ""); + + void LogWarn(string className, string message, [CallerMemberName] string methodName = ""); + + void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = ""); + + T LoadJsonStorage(PluginMetadata metadata) where T : new(); + + void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new(); } } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 0afa67d1a..9c36a709e 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -18,6 +18,9 @@ using System.Threading; using System.IO; using Flow.Launcher.Infrastructure.Http; using JetBrains.Annotations; +using System.Runtime.CompilerServices; +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Storage; namespace Flow.Launcher { @@ -160,6 +163,41 @@ namespace Flow.Launcher { PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); } + + + public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") + { + Log.Debug(className, message, methodName); + } + + public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") + { + Log.Info(className, message, methodName); + } + + public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") + { + Log.Warn(className, message, methodName); + } + + public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") + { + Log.Exception(className, message, e, methodName); + } + + public T LoadJsonStorage(PluginMetadata metadata) where T : new() + { + var storage = new PluginJsonStorage(); + return storage.Load(); + } + + public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() + { + var storage = new PluginJsonStorage(setting); + storage.Save(); + } + + #endregion #region Private Methods @@ -173,6 +211,7 @@ namespace Flow.Launcher return true; } + #endregion } } From 9fb44e6002d62227ffef1828c6e33c85a2ad6758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 18:12:57 +0800 Subject: [PATCH 37/56] format code --- Flow.Launcher/PublicAPIInstance.cs | 97 ++++++------------------------ 1 file changed, 19 insertions(+), 78 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 9c36a709e..967cf1bf7 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -70,15 +70,9 @@ namespace Flow.Launcher UpdateManager.RestartApp(Constant.ApplicationFileName); } - public void RestarApp() - { - RestartApp(); - } + public void RestarApp() => RestartApp(); - public void CheckForNewUpdate() - { - _settingsVM.UpdateApp(); - } + public void CheckForNewUpdate() => _settingsVM.UpdateApp(); public void SaveAppAllSettings() { @@ -88,15 +82,9 @@ namespace Flow.Launcher ImageLoader.Save(); } - public Task ReloadAllPluginData() - { - return PluginManager.ReloadData(); - } + public Task ReloadAllPluginData() => PluginManager.ReloadData(); - public void ShowMsg(string title, string subTitle = "", string iconPath = "") - { - ShowMsg(title, subTitle, iconPath, true); - } + public void ShowMsg(string title, string subTitle = "", string iconPath = "") => ShowMsg(title, subTitle, iconPath, true); public void ShowMsg(string title, string subTitle, string iconPath, bool useMainWindowAsOwner = true) { @@ -115,87 +103,40 @@ namespace Flow.Launcher }); } - public void StartLoadingBar() - { - _mainVM.ProgressBarVisibility = Visibility.Visible; - } + public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible; - public void StopLoadingBar() - { - _mainVM.ProgressBarVisibility = Visibility.Collapsed; - } + public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed; - public string GetTranslation(string key) - { - return InternationalizationManager.Instance.GetTranslation(key); - } + public string GetTranslation(string key) => InternationalizationManager.Instance.GetTranslation(key); - public List GetAllPlugins() - { - return PluginManager.AllPlugins.ToList(); - } + public List GetAllPlugins() => PluginManager.AllPlugins.ToList(); public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; public MatchResult FuzzySearch(string query, string stringToCompare) => StringMatcher.FuzzySearch(query, stringToCompare); - public Task HttpGetStringAsync(string url, CancellationToken token = default) - { - return Http.GetAsync(url); - } + public Task HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url); - public Task HttpGetStreamAsync(string url, CancellationToken token = default) - { - return Http.GetStreamAsync(url); - } + public Task HttpGetStreamAsync(string url, CancellationToken token = default) => Http.GetStreamAsync(url); - public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath) - { - return Http.DownloadAsync(url, filePath); - } + public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath) => Http.DownloadAsync(url, filePath); - public void AddActionKeyword(string pluginId, string newActionKeyword) - { - PluginManager.AddActionKeyword(pluginId, newActionKeyword); - } + public void AddActionKeyword(string pluginId, string newActionKeyword) => PluginManager.AddActionKeyword(pluginId, newActionKeyword); - public void RemoveActionKeyword(string pluginId, string oldActionKeyword) - { - PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); - } + public void RemoveActionKeyword(string pluginId, string oldActionKeyword) => PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); - public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") - { - Log.Debug(className, message, methodName); - } + public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") => Log.Debug(className, message, methodName); - public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") - { - Log.Info(className, message, methodName); - } + public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") => Log.Info(className, message, methodName); - public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") - { - Log.Warn(className, message, methodName); - } + public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") => Log.Warn(className, message, methodName); - public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") - { - Log.Exception(className, message, e, methodName); - } + public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName); - public T LoadJsonStorage(PluginMetadata metadata) where T : new() - { - var storage = new PluginJsonStorage(); - return storage.Load(); - } + public T LoadJsonStorage(PluginMetadata metadata) where T : new() => new PluginJsonStorage().Load(); - public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() - { - var storage = new PluginJsonStorage(setting); - storage.Save(); - } + public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() => new PluginJsonStorage(setting).Save(); #endregion From 71ca3bbf38b4fd55aca0540530d812d6df9d7011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 18:15:46 +0800 Subject: [PATCH 38/56] formatting --- Flow.Launcher/PublicAPIInstance.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 967cf1bf7..327ee6658 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -111,8 +111,6 @@ namespace Flow.Launcher public List GetAllPlugins() => PluginManager.AllPlugins.ToList(); - public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; - public MatchResult FuzzySearch(string query, string stringToCompare) => StringMatcher.FuzzySearch(query, stringToCompare); public Task HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url); @@ -125,7 +123,6 @@ namespace Flow.Launcher public void RemoveActionKeyword(string pluginId, string oldActionKeyword) => PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); - public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") => Log.Debug(className, message, methodName); public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") => Log.Info(className, message, methodName); @@ -138,6 +135,7 @@ namespace Flow.Launcher public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() => new PluginJsonStorage(setting).Save(); + public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; #endregion From 8a26471c4fca63ce8b413d69ce3f32fd1849f389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 18:25:09 +0800 Subject: [PATCH 39/56] Update Document and remove unused parameter --- Flow.Launcher.Plugin/IPublicAPI.cs | 61 +++++++++++++++++++++++++++++- Flow.Launcher/PublicAPIInstance.cs | 4 +- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index 2a69a2495..a3f711a17 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -89,28 +89,85 @@ namespace Flow.Launcher.Plugin /// event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; + /// + /// Fuzzy Search the string with query + /// + /// Query String + /// The string to Search for Query + /// Match results MatchResult FuzzySearch(string query, string stringToCompare); + /// + /// Http Get to the spefic URL + /// + /// URL to call Http Get + /// Cancellation Token + /// Task to get string result Task HttpGetStringAsync(string url, CancellationToken token = default); + /// + /// Http Get to the spefic URL + /// + /// URL to call Http Get + /// Cancellation Token + /// Task to get stream result Task HttpGetStreamAsync(string url, CancellationToken token = default); + /// + /// Download the specific url to a cretain file path + /// + /// URL to download file + /// place to store file + /// Task showing the progress Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath); + /// + /// Add ActionKeyword for specific plugin + /// + /// ID for plugin that needs to add action keyword + /// The actionkeyword that is supposed to be added void AddActionKeyword(string pluginId, string newActionKeyword); + /// + /// Remove ActionKeyword for specific plugin + /// + /// ID for plugin that needs to remove action keyword + /// The actionkeyword that is supposed to be removed void RemoveActionKeyword(string pluginId, string oldActionKeyword); + /// + /// Log Debug message + /// Message will only be logged in Debug mode + /// void LogDebug(string className, string message, [CallerMemberName] string methodName = ""); + /// + /// Log Message + /// void LogInfo(string className, string message, [CallerMemberName] string methodName = ""); + /// + /// Log Warning + /// void LogWarn(string className, string message, [CallerMemberName] string methodName = ""); + /// + /// Log an Exception + /// void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = ""); - T LoadJsonStorage(PluginMetadata metadata) where T : new(); + /// + /// Load JsonStorage for current plugin + /// + /// Type for deserialization + /// + T LoadJsonStorage() where T : new(); - void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new(); + /// + /// Save JsonStorage for current plugin + /// + /// Type for Serialization + /// + void SaveJsonStorage(T setting) where T : new(); } } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 327ee6658..200eb166a 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -131,9 +131,9 @@ namespace Flow.Launcher public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName); - public T LoadJsonStorage(PluginMetadata metadata) where T : new() => new PluginJsonStorage().Load(); + public T LoadJsonStorage() where T : new() => new PluginJsonStorage().Load(); - public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() => new PluginJsonStorage(setting).Save(); + public void SaveJsonStorage(T setting) where T : new() => new PluginJsonStorage(setting).Save(); public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; From 1ec069bfcf1a982de6a2222bc70bcfe8eebfdf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 22:36:05 +0800 Subject: [PATCH 40/56] Use HttpCompletionOption.ResponseHeadersRead for Stream reading and downloading --- Flow.Launcher.Infrastructure/Http/Http.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index de2e82359..3a3e770a5 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -75,7 +75,7 @@ namespace Flow.Launcher.Infrastructure.Http { try { - using var response = await client.GetAsync(url, token); + using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); if (response.StatusCode == HttpStatusCode.OK) { await using var fileStream = new FileStream(filePath, FileMode.CreateNew); @@ -135,7 +135,7 @@ namespace Flow.Launcher.Infrastructure.Http public static async Task GetStreamAsync([NotNull] string url, CancellationToken token = default) { Log.Debug($"|Http.Get|Url <{url}>"); - var response = await client.GetAsync(url, token); + var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); return await response.Content.ReadAsStreamAsync(); } } From 20c0b1788ffda2c5ac38589f828912cb5bc80137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 22:43:11 +0800 Subject: [PATCH 41/56] Add Cancellation to IPublicAPI download method --- Flow.Launcher.Plugin/IPublicAPI.cs | 2 +- Flow.Launcher/PublicAPIInstance.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index a3f711a17..27a65191a 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -119,7 +119,7 @@ namespace Flow.Launcher.Plugin /// URL to download file /// place to store file /// Task showing the progress - Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath); + Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default); /// /// Add ActionKeyword for specific plugin diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 200eb166a..6c55e37fe 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -117,7 +117,7 @@ namespace Flow.Launcher public Task HttpGetStreamAsync(string url, CancellationToken token = default) => Http.GetStreamAsync(url); - public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath) => Http.DownloadAsync(url, filePath); + public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default) => Http.DownloadAsync(url, filePath, token); public void AddActionKeyword(string pluginId, string newActionKeyword) => PluginManager.AddActionKeyword(pluginId, newActionKeyword); From 14e51c07310fd38d5e0a93964bf04a4c77ab9695 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 06:42:18 +1100 Subject: [PATCH 42/56] update comments --- Flow.Launcher.Plugin/IPublicAPI.cs | 23 ++++++++++++----------- Flow.Launcher/PublicAPIInstance.cs | 1 - 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index 27a65191a..07e2b2700 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -90,15 +90,15 @@ namespace Flow.Launcher.Plugin event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; /// - /// Fuzzy Search the string with query + /// Fuzzy Search the string with the given query. This is the core search mechanism Flow uses /// - /// Query String - /// The string to Search for Query + /// Query string + /// The string that will be compared against the query /// Match results MatchResult FuzzySearch(string query, string stringToCompare); /// - /// Http Get to the spefic URL + /// Http download the spefic url and return as string /// /// URL to call Http Get /// Cancellation Token @@ -106,7 +106,7 @@ namespace Flow.Launcher.Plugin Task HttpGetStringAsync(string url, CancellationToken token = default); /// - /// Http Get to the spefic URL + /// Http download the spefic url and return as stream /// /// URL to call Http Get /// Cancellation Token @@ -136,35 +136,36 @@ namespace Flow.Launcher.Plugin void RemoveActionKeyword(string pluginId, string oldActionKeyword); /// - /// Log Debug message + /// Log debug message /// Message will only be logged in Debug mode /// void LogDebug(string className, string message, [CallerMemberName] string methodName = ""); /// - /// Log Message + /// Log info message /// void LogInfo(string className, string message, [CallerMemberName] string methodName = ""); /// - /// Log Warning + /// Log warning message /// void LogWarn(string className, string message, [CallerMemberName] string methodName = ""); /// - /// Log an Exception + /// Log an Exception. Will throw if in debug mode so developer will be aware, + /// otherwise logs the eror message. This is the primary logging method used for Flow /// void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = ""); /// - /// Load JsonStorage for current plugin + /// Load JsonStorage for current plugin. This is the method used to load settings from json in Flow /// /// Type for deserialization /// T LoadJsonStorage() where T : new(); /// - /// Save JsonStorage for current plugin + /// Save JsonStorage for current plugin. This is the method used to save settings to json in Flow /// /// Type for Serialization /// diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 6c55e37fe..9b41c2c05 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -150,7 +150,6 @@ namespace Flow.Launcher return true; } - #endregion } } From 2b6b5e9d8bdae77b5ef8be2feb0abf47e5a7bf46 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 07:05:46 +1100 Subject: [PATCH 43/56] version bump --- Plugins/Flow.Launcher.Plugin.Program/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index f713a33ec..93e5079c2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "1.4.0", + "Version": "1.4.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", From ba5cb5788e61b8e6eefbef21fa12a32f40368fd0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 07:18:16 +1100 Subject: [PATCH 44/56] remove unused variables --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 1 - Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 22f4aea59..bd09cd9c6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -228,7 +228,6 @@ namespace Flow.Launcher.Plugin.Program public static void StartProcess(Func runProcess, ProcessStartInfo info) { - bool hide; try { runProcess(info); diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index d73483818..20df233bb 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -33,7 +33,6 @@ namespace Flow.Launcher.Plugin.Program.Programs public string Location => ParentDirectory; private const string ShortcutExtension = "lnk"; - private const string ApplicationReferenceExtension = "appref-ms"; private const string ExeExtension = "exe"; From 22bdbc93675df9485a8e4e9bad3f6c281d56a90c Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 08:19:48 +1100 Subject: [PATCH 45/56] add match on path --- .../Programs/Win32.cs | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 20df233bb..3c0d1954c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -41,36 +41,38 @@ namespace Flow.Launcher.Plugin.Program.Programs string title; MatchResult matchResult; + var nameMatchResult = StringMatcher.FuzzySearch(query, Name); + var descriptionMatchResult = StringMatcher.FuzzySearch(query, Description); + var pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); + + var score = new[] { nameMatchResult.Score, descriptionMatchResult.Score, pathMatchResult.Score }.Max(); + + if (score < (int)StringMatcher.Instance.UserSettingSearchPrecision) + return null; + // We suppose Name won't be null - if (Description == null || Name.StartsWith(Description)) + if (score == nameMatchResult.Score && (Description == null || Name.StartsWith(Description))) { title = Name; - matchResult = StringMatcher.FuzzySearch(query, title); + matchResult = nameMatchResult; } - else if (Description.StartsWith(Name)) + else if (score == descriptionMatchResult.Score && Description.StartsWith(Name)) { title = Description; - matchResult = StringMatcher.FuzzySearch(query, Description); + + for (int i = 0; i < descriptionMatchResult.MatchData.Count; i++) + { + descriptionMatchResult.MatchData[i] += Name.Length + 2; // 2 is ": " + } + + matchResult = descriptionMatchResult; } else { title = $"{Name}: {Description}"; - var nameMatch = StringMatcher.FuzzySearch(query, Name); - var desciptionMatch = StringMatcher.FuzzySearch(query, Description); - if (desciptionMatch.Score > nameMatch.Score) - { - for (int i = 0; i < desciptionMatch.MatchData.Count; i++) - { - desciptionMatch.MatchData[i] += Name.Length + 2; // 2 is ": " - } - matchResult = desciptionMatch; - } - else matchResult = nameMatch; + matchResult = pathMatchResult; } - if (!matchResult.Success) return null; - - var result = new Result { Title = title, From ad998b17886d0b4553eb53364ce86bec0172ce07 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 14:01:07 +1100 Subject: [PATCH 46/56] add only path string match when lnk programs --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 3c0d1954c..74b85fd46 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -43,7 +43,10 @@ namespace Flow.Launcher.Plugin.Program.Programs var nameMatchResult = StringMatcher.FuzzySearch(query, Name); var descriptionMatchResult = StringMatcher.FuzzySearch(query, Description); - var pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); + + var pathMatchResult = new MatchResult(false, 0, new List(), 0); + if ((LnkResolvedPath ?? FullPath) != FullPath) + pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); var score = new[] { nameMatchResult.Score, descriptionMatchResult.Score, pathMatchResult.Score }.Max(); From 79d195f7e656dbf38fd5383fccf3cf0ecc97622e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 11:49:18 +0800 Subject: [PATCH 47/56] Change logic --- .../Programs/Win32.cs | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 74b85fd46..275726595 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -39,41 +39,52 @@ namespace Flow.Launcher.Plugin.Program.Programs public Result Result(string query, IPublicAPI api) { string title; - MatchResult matchResult; var nameMatchResult = StringMatcher.FuzzySearch(query, Name); var descriptionMatchResult = StringMatcher.FuzzySearch(query, Description); var pathMatchResult = new MatchResult(false, 0, new List(), 0); - if ((LnkResolvedPath ?? FullPath) != FullPath) + if (ExecutableName != null) // only lnk program will need this one pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); - var score = new[] { nameMatchResult.Score, descriptionMatchResult.Score, pathMatchResult.Score }.Max(); + MatchResult matchResult = nameMatchResult; - if (score < (int)StringMatcher.Instance.UserSettingSearchPrecision) - return null; + if (nameMatchResult.Score < descriptionMatchResult.Score) + matchResult = descriptionMatchResult; + + if (!matchResult.IsSearchPrecisionScoreMet()) + { + if (pathMatchResult.IsSearchPrecisionScoreMet()) + matchResult = pathMatchResult; + else return null; + } // We suppose Name won't be null - if (score == nameMatchResult.Score && (Description == null || Name.StartsWith(Description))) + if (Description == null || Name.StartsWith(Description)) { title = Name; - matchResult = nameMatchResult; } - else if (score == descriptionMatchResult.Score && Description.StartsWith(Name)) + else if (Description.StartsWith(Name)) { title = Description; - - for (int i = 0; i < descriptionMatchResult.MatchData.Count; i++) - { - descriptionMatchResult.MatchData[i] += Name.Length + 2; // 2 is ": " - } - - matchResult = descriptionMatchResult; } else { title = $"{Name}: {Description}"; - matchResult = pathMatchResult; + + if (matchResult == descriptionMatchResult) + { + for (int i = 0; i < descriptionMatchResult.MatchData.Count; i++) + { + matchResult.MatchData[i] += Name.Length + 2; // 2 is ": " + } + } + } + + if (matchResult == pathMatchResult) + { + // path Match won't have valid highlight data + matchResult.MatchData = new List(); } var result = new Result @@ -175,7 +186,7 @@ namespace Flow.Launcher.Plugin.Program.Programs public override string ToString() { - return ExecutableName; + return Name; } private static Win32 Win32Program(string path) @@ -200,7 +211,7 @@ namespace Flow.Launcher.Plugin.Program.Programs ProgramLogger.LogException($"|Win32|Win32Program|{path}" + $"|Permission denied when trying to load the program from {path}", e); - return new Win32() {Valid = false, Enabled = false}; + return new Win32() { Valid = false, Enabled = false }; } } @@ -278,7 +289,7 @@ namespace Flow.Launcher.Plugin.Program.Programs ProgramLogger.LogException($"|Win32|ExeProgram|{path}" + $"|Permission denied when trying to load the program from {path}", e); - return new Win32() {Valid = false, Enabled = false}; + return new Win32() { Valid = false, Enabled = false }; } } @@ -420,7 +431,6 @@ namespace Flow.Launcher.Plugin.Program.Programs return null; var entry = Win32Program(path); - entry.ExecutableName = Path.GetFileName(path); return entry; } @@ -450,7 +460,7 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - public static IEnumerable DistinctBy(IEnumerable source, Func selector) + public static IEnumerable DistinctBy(IEnumerable source, Func selector) { var set = new HashSet(); foreach (var item in source) From 208bde0f99539c2cc40ce6473e364db53196497f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 11:50:27 +0800 Subject: [PATCH 48/56] Use ExecutableName instead of getting it from path --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 275726595..372d36524 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -45,7 +45,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var pathMatchResult = new MatchResult(false, 0, new List(), 0); if (ExecutableName != null) // only lnk program will need this one - pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); + pathMatchResult = StringMatcher.FuzzySearch(query, ExecutableName); MatchResult matchResult = nameMatchResult; From 7e4ac1ce8f74f6809feedf4aa2fd0af6a084cad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 11:58:33 +0800 Subject: [PATCH 49/56] fix formatting --- .../Programs/ApplicationActivationHelper.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs index 3023836c1..4b8423ed6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs @@ -21,9 +21,9 @@ namespace Flow.Launcher.Plugin.Program.Programs [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IApplicationActivationManager { - IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId); - IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); - IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); + IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); + IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); + IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); } // Application Activation Manager Class @@ -32,13 +32,13 @@ namespace Flow.Launcher.Plugin.Program.Programs { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/] - public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId); + public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); + public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); + public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); } } } From 4b4953e3ac78c17f94a9bc09f957d8880a33da0d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 20:02:18 +1100 Subject: [PATCH 50/56] version bump Program plugin --- Plugins/Flow.Launcher.Plugin.Program/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index 93e5079c2..d110124ff 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "1.4.1", + "Version": "1.4.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", From 575ff31ce56e056ac4f97f0cb5faa1c46d20f612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 17:17:03 +0800 Subject: [PATCH 51/56] change format Initialize out variable inline, and use discard varable for unused variable --- .../Programs/UWP.cs | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 69afecb4f..855a11c86 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -56,10 +56,9 @@ namespace Flow.Launcher.Plugin.Program.Programs var namespaces = XmlNamespaces(path); InitPackageVersion(namespaces); - IStream stream; const uint noAttribute = 0x80; const Stgm exclusiveRead = Stgm.Read | Stgm.ShareExclusive; - var hResult = SHCreateStreamOnFileEx(path, exclusiveRead, noAttribute, false, null, out stream); + var hResult = SHCreateStreamOnFileEx(path, exclusiveRead, noAttribute, false, null, out IStream stream); if (hResult == Hresult.Ok) { @@ -345,14 +344,13 @@ namespace Flow.Launcher.Plugin.Program.Programs private async void Launch(IPublicAPI api) { var appManager = new ApplicationActivationHelper.ApplicationActivationManager(); - uint unusedPid; const string noArgs = ""; const ApplicationActivationHelper.ActivateOptions noFlags = ApplicationActivationHelper.ActivateOptions.None; await Task.Run(() => { try { - appManager.ActivateApplication(UserModelId, noArgs, noFlags, out unusedPid); + appManager.ActivateApplication(UserModelId, noArgs, noFlags, out _); } catch (Exception) { @@ -366,19 +364,13 @@ namespace Flow.Launcher.Plugin.Program.Programs public Application(AppxPackageHelper.IAppxManifestApplication manifestApp, UWP package) { // This is done because we cannot use the keyword 'out' along with a property - string tmpUserModelId; - string tmpUniqueIdentifier; - string tmpDisplayName; - string tmpDescription; - string tmpBackgroundColor; - string tmpEntryPoint; - manifestApp.GetAppUserModelId(out tmpUserModelId); - manifestApp.GetAppUserModelId(out tmpUniqueIdentifier); - manifestApp.GetStringValue("DisplayName", out tmpDisplayName); - manifestApp.GetStringValue("Description", out tmpDescription); - manifestApp.GetStringValue("BackgroundColor", out tmpBackgroundColor); - manifestApp.GetStringValue("EntryPoint", out tmpEntryPoint); + manifestApp.GetAppUserModelId(out string tmpUserModelId); + manifestApp.GetAppUserModelId(out string tmpUniqueIdentifier); + manifestApp.GetStringValue("DisplayName", out string tmpDisplayName); + manifestApp.GetStringValue("Description", out string tmpDescription); + manifestApp.GetStringValue("BackgroundColor", out string tmpBackgroundColor); + manifestApp.GetStringValue("EntryPoint", out string tmpEntryPoint); UserModelId = tmpUserModelId; UniqueIdentifier = tmpUniqueIdentifier; @@ -464,9 +456,8 @@ namespace Flow.Launcher.Plugin.Program.Programs }; if (logoKeyFromVersion.ContainsKey(Package.Version)) { - string logoUri; var key = logoKeyFromVersion[Package.Version]; - app.GetStringValue(key, out logoUri); + app.GetStringValue(key, out string logoUri); return logoUri; } else From 8a2a71000ec369bf17d57d3c9311840e731ea41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 17:58:15 +0800 Subject: [PATCH 52/56] Use Channel instead of BufferBlock --- Flow.Launcher/ViewModel/MainViewModel.cs | 31 ++++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index a12912b67..0cf4b11dc 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -19,6 +19,7 @@ using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; using Flow.Launcher.Infrastructure.Logger; +using System.Threading.Channels; namespace Flow.Launcher.ViewModel { @@ -46,7 +47,7 @@ namespace Flow.Launcher.ViewModel private readonly Internationalization _translator = InternationalizationManager.Instance; - private BufferBlock _resultsUpdateQueue; + private Channel _resultsUpdateQueue; private Task _resultsViewUpdateTask; #endregion @@ -85,18 +86,21 @@ namespace Flow.Launcher.ViewModel private void RegisterViewUpdate() { - _resultsUpdateQueue = new BufferBlock(); + _resultsUpdateQueue = Channel.CreateUnbounded(); _resultsViewUpdateTask = Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); async Task updateAction() { var queue = new Dictionary(); - while (await _resultsUpdateQueue.OutputAvailableAsync()) + var queueReader = _resultsUpdateQueue.Reader; + + // it is not supposed to be false because it won't be complete + while (await queueReader.WaitToReadAsync()) { queue.Clear(); await Task.Delay(20); - while (_resultsUpdateQueue.TryReceive(out var item)) + await foreach (var item in queueReader.ReadAllAsync()) { if (!item.Token.IsCancellationRequested) queue[item.ID] = item; @@ -104,6 +108,8 @@ namespace Flow.Launcher.ViewModel UpdateResultView(queue.Values); } + + Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); }; void continueAction(Task t) @@ -128,7 +134,10 @@ namespace Flow.Launcher.ViewModel if (e.Query.RawQuery == QueryText) // TODO: allow cancellation { PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - _resultsUpdateQueue.Post(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken)); + if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken))) + { + Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); + }; } }; } @@ -529,9 +538,15 @@ namespace Flow.Launcher.ViewModel await Task.Yield(); var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken); - if (!currentCancellationToken.IsCancellationRequested && results != null) - _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, query, - currentCancellationToken)); + if (currentCancellationToken.IsCancellationRequested || results == null) + { + return; + } + + if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) + { + Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); + }; } }, currentCancellationToken) .ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), From d36140c51ce0b3c1a729092c1efff8f218e59582 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 21:14:50 +1100 Subject: [PATCH 53/56] version bump PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 4cba7ec83..f95c0d60d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.6.4", + "Version": "1.7.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From a7811f312c1add5f6a16e44970ebd010173abed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 18:19:30 +0800 Subject: [PATCH 54/56] fix an issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 0cf4b11dc..3e71adcd1 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -98,15 +98,15 @@ namespace Flow.Launcher.ViewModel // it is not supposed to be false because it won't be complete while (await queueReader.WaitToReadAsync()) { - queue.Clear(); await Task.Delay(20); - await foreach (var item in queueReader.ReadAllAsync()) + while (queueReader.TryRead(out var item)) { if (!item.Token.IsCancellationRequested) queue[item.ID] = item; } UpdateResultView(queue.Values); + queue.Clear(); } Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); From b017bdeed435f810909e2afd2c9f49bb41ba3f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 18:37:42 +0800 Subject: [PATCH 55/56] fix format --- Flow.Launcher/ViewModel/MainViewModel.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3e71adcd1..90bdc38c8 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -538,10 +538,7 @@ namespace Flow.Launcher.ViewModel await Task.Yield(); var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken); - if (currentCancellationToken.IsCancellationRequested || results == null) - { - return; - } + if (currentCancellationToken.IsCancellationRequested || results == null) return; if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) { From 350499a4801a9a60ba00a811b79bc7785b4da33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 02:12:28 +0800 Subject: [PATCH 56/56] Optimize Logic and code --- Flow.Launcher/ViewModel/MainViewModel.cs | 17 +++++++++-------- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 9 +-------- Flow.Launcher/ViewModel/ResultsViewModel.cs | 11 ++++------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 90bdc38c8..c1382e51e 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -47,7 +47,7 @@ namespace Flow.Launcher.ViewModel private readonly Internationalization _translator = InternationalizationManager.Instance; - private Channel _resultsUpdateQueue; + private ChannelWriter _resultsUpdateChannelWriter; private Task _resultsViewUpdateTask; #endregion @@ -86,20 +86,21 @@ namespace Flow.Launcher.ViewModel private void RegisterViewUpdate() { - _resultsUpdateQueue = Channel.CreateUnbounded(); + var resultUpdateChannel = Channel.CreateUnbounded(); + _resultsUpdateChannelWriter = resultUpdateChannel.Writer; _resultsViewUpdateTask = Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); async Task updateAction() { var queue = new Dictionary(); - var queueReader = _resultsUpdateQueue.Reader; + var channelReader = resultUpdateChannel.Reader; // it is not supposed to be false because it won't be complete - while (await queueReader.WaitToReadAsync()) + while (await channelReader.WaitToReadAsync()) { await Task.Delay(20); - while (queueReader.TryRead(out var item)) + while (channelReader.TryRead(out var item)) { if (!item.Token.IsCancellationRequested) queue[item.ID] = item; @@ -134,7 +135,7 @@ namespace Flow.Launcher.ViewModel if (e.Query.RawQuery == QueryText) // TODO: allow cancellation { PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); }; @@ -539,8 +540,8 @@ namespace Flow.Launcher.ViewModel var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken); if (currentCancellationToken.IsCancellationRequested || results == null) return; - - if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) + + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); }; diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index be48f53c1..87d526fd6 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -6,7 +6,7 @@ using System.Threading; namespace Flow.Launcher.ViewModel { - public class ResultsForUpdate + public struct ResultsForUpdate { public List Results { get; } @@ -16,13 +16,6 @@ namespace Flow.Launcher.ViewModel public Query Query { get; } public CancellationToken Token { get; } - public ResultsForUpdate(List results, string resultID, CancellationToken token) - { - Results = results; - ID = resultID; - Token = token; - } - public ResultsForUpdate(List results, PluginMetadata metadata, Query query, CancellationToken token) { Results = results; diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index feab3a751..41f16f4f2 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -183,13 +183,12 @@ namespace Flow.Launcher.ViewModel private List NewResults(List newRawResults, string resultId) { if (newRawResults.Count == 0) - return Results.ToList(); + return Results; - var results = Results as IEnumerable; var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings)); - return results.Where(r => r.Result.PluginID != resultId) + return Results.Where(r => r.Result.PluginID != resultId) .Concat(newResults) .OrderByDescending(r => r.Result.Score) .ToList(); @@ -198,11 +197,9 @@ namespace Flow.Launcher.ViewModel private List NewResults(IEnumerable resultsForUpdates) { if (!resultsForUpdates.Any()) - return Results.ToList(); + return Results; - var results = Results as IEnumerable; - - return results.Where(r => r != null && !resultsForUpdates.Any(u => u.Metadata.ID == r.Result.PluginID)) + return Results.Where(r => r != null && !resultsForUpdates.Any(u => u.ID == r.Result.PluginID)) .Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings))) .OrderByDescending(rv => rv.Result.Score) .ToList();