mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Reindex when app install or deleted (and uwp update).
This commit is contained in:
parent
78f3008c32
commit
73056534bb
3 changed files with 198 additions and 52 deletions
|
|
@ -18,7 +18,7 @@ using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
|||
|
||||
namespace Flow.Launcher.Plugin.Program
|
||||
{
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISavable, IAsyncReloadable
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISavable, IAsyncReloadable, IDisposable
|
||||
{
|
||||
internal static Win32[] _win32s { get; set; }
|
||||
internal static UWP.Application[] _uwps { get; set; }
|
||||
|
|
@ -126,6 +126,9 @@ namespace Flow.Launcher.Plugin.Program
|
|||
|
||||
if (!(_win32s.Any() && _uwps.Any()))
|
||||
await indexTask;
|
||||
|
||||
Win32.WatchProgramUpdate(_settings);
|
||||
UWP.WatchUWPInstallation();
|
||||
}
|
||||
|
||||
public static void IndexWin32Programs()
|
||||
|
|
@ -209,13 +212,11 @@ namespace Flow.Launcher.Plugin.Program
|
|||
return;
|
||||
|
||||
if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
|
||||
_uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)
|
||||
.FirstOrDefault()
|
||||
_uwps.FirstOrDefault(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)!
|
||||
.Enabled = false;
|
||||
|
||||
if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
|
||||
_win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)
|
||||
.FirstOrDefault()
|
||||
_win32s.FirstOrDefault(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)!
|
||||
.Enabled = false;
|
||||
|
||||
_settings.DisabledProgramSources
|
||||
|
|
@ -248,5 +249,10 @@ namespace Flow.Launcher.Plugin.Program
|
|||
{
|
||||
await IndexPrograms();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Win32.Dispose();
|
||||
UWP.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
{
|
||||
var e = Marshal.GetExceptionForHR((int)hResult);
|
||||
ProgramLogger.LogException($"|UWP|InitializeAppInfo|{path}" +
|
||||
"|Error caused while trying to get the details of the UWP program", e);
|
||||
"|Error caused while trying to get the details of the UWP program", e);
|
||||
|
||||
Apps = new List<Application>().ToArray();
|
||||
}
|
||||
|
|
@ -91,28 +91,29 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
|
||||
|
||||
|
||||
|
||||
/// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx
|
||||
private string[] XmlNamespaces(string path)
|
||||
{
|
||||
XDocument z = XDocument.Load(path);
|
||||
if (z.Root != null)
|
||||
{
|
||||
var namespaces = z.Root.Attributes().
|
||||
Where(a => a.IsNamespaceDeclaration).
|
||||
GroupBy(
|
||||
a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName,
|
||||
a => XNamespace.Get(a.Value)
|
||||
).Select(
|
||||
g => g.First().ToString()
|
||||
).ToArray();
|
||||
var namespaces = z.Root.Attributes().Where(a => a.IsNamespaceDeclaration).GroupBy(
|
||||
a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName,
|
||||
a => XNamespace.Get(a.Value)
|
||||
).Select(
|
||||
g => g.First().ToString()
|
||||
).ToArray();
|
||||
return namespaces;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|XmlNamespaces|{path}" +
|
||||
$"|Error occured while trying to get the XML from {path}", new ArgumentNullException());
|
||||
$"|Error occured while trying to get the XML from {path}", new ArgumentNullException());
|
||||
|
||||
return new string[] { };
|
||||
return new string[]
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,9 +121,15 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
{
|
||||
var versionFromNamespace = new Dictionary<string, PackageVersion>
|
||||
{
|
||||
{"http://schemas.microsoft.com/appx/manifest/foundation/windows10", PackageVersion.Windows10},
|
||||
{"http://schemas.microsoft.com/appx/2013/manifest", PackageVersion.Windows81},
|
||||
{"http://schemas.microsoft.com/appx/2010/manifest", PackageVersion.Windows8},
|
||||
{
|
||||
"http://schemas.microsoft.com/appx/manifest/foundation/windows10", PackageVersion.Windows10
|
||||
},
|
||||
{
|
||||
"http://schemas.microsoft.com/appx/2013/manifest", PackageVersion.Windows81
|
||||
},
|
||||
{
|
||||
"http://schemas.microsoft.com/appx/2010/manifest", PackageVersion.Windows8
|
||||
},
|
||||
};
|
||||
|
||||
foreach (var n in versionFromNamespace.Keys)
|
||||
|
|
@ -135,8 +142,8 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
|
||||
ProgramLogger.LogException($"|UWP|XmlNamespaces|{Location}" +
|
||||
"|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version "
|
||||
+ $"{FullName} from location {Location} is returned.", new FormatException());
|
||||
"|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version "
|
||||
+ $"{FullName} from location {Location} is returned.", new FormatException());
|
||||
|
||||
Version = PackageVersion.Unknown;
|
||||
}
|
||||
|
|
@ -172,15 +179,17 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}).ToArray();
|
||||
|
||||
var updatedListWithoutDisabledApps = applications
|
||||
.Where(t1 => !Main._settings.DisabledProgramSources
|
||||
.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
|
||||
.Select(x => x);
|
||||
.Where(t1 => !Main._settings.DisabledProgramSources
|
||||
.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
|
||||
.Select(x => x);
|
||||
|
||||
return updatedListWithoutDisabledApps.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Application[] { };
|
||||
return new Application[]
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +225,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.LogException("UWP", "CurrentUserPackages", $"id", "An unexpected error occured and "
|
||||
+ $"unable to verify if package is valid", e);
|
||||
+ $"unable to verify if package is valid", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -226,10 +235,30 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
else
|
||||
{
|
||||
return new Package[] { };
|
||||
return new Package[]
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static List<FileSystemWatcher> _watchers = new();
|
||||
|
||||
private static void GenerateWatcher(string path)
|
||||
{
|
||||
var watcher = new FileSystemWatcher(path);
|
||||
watcher.Created += static (_, _) => Task.Run(Main.IndexUwpPrograms);
|
||||
watcher.Deleted += static (_, _) => Task.Run(Main.IndexUwpPrograms);
|
||||
watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
public static void WatchUWPInstallation()
|
||||
{
|
||||
PackageCatalog.OpenForCurrentUser().PackageStatusChanged += (_, _) =>
|
||||
{
|
||||
Task.Delay(10000).ContinueWith(t => Main.IndexUwpPrograms());
|
||||
};
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return FamilyName;
|
||||
|
|
@ -326,7 +355,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
e.SpecialKeyState.ShiftPressed &&
|
||||
!e.SpecialKeyState.AltPressed &&
|
||||
!e.SpecialKeyState.WinPressed
|
||||
);
|
||||
);
|
||||
|
||||
if (elevated && CanRunElevated)
|
||||
{
|
||||
|
|
@ -359,14 +388,12 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
new Result
|
||||
{
|
||||
Title = api.GetTranslation("flowlauncher_plugin_program_open_containing_folder"),
|
||||
|
||||
Action = _ =>
|
||||
{
|
||||
Main.Context.API.OpenDirectory(Package.Location);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
IcoPath = "Images/folder.png"
|
||||
}
|
||||
};
|
||||
|
|
@ -415,8 +442,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
|
||||
var info = new ProcessStartInfo(command)
|
||||
{
|
||||
UseShellExecute = true,
|
||||
Verb = "runas",
|
||||
UseShellExecute = true, Verb = "runas",
|
||||
};
|
||||
|
||||
Main.StartProcess(Process.Start, info);
|
||||
|
|
@ -493,7 +519,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Can't load null or empty result "
|
||||
+ $"pri {source} in uwp location {Package.Location}", new NullReferenceException());
|
||||
+ $"pri {source} in uwp location {Package.Location}", new NullReferenceException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
@ -533,9 +559,15 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
{
|
||||
var logoKeyFromVersion = new Dictionary<PackageVersion, string>
|
||||
{
|
||||
{ PackageVersion.Windows10, "Square44x44Logo" },
|
||||
{ PackageVersion.Windows81, "Square30x30Logo" },
|
||||
{ PackageVersion.Windows8, "SmallLogo" },
|
||||
{
|
||||
PackageVersion.Windows10, "Square44x44Logo"
|
||||
},
|
||||
{
|
||||
PackageVersion.Windows81, "Square30x30Logo"
|
||||
},
|
||||
{
|
||||
PackageVersion.Windows8, "SmallLogo"
|
||||
},
|
||||
};
|
||||
if (logoKeyFromVersion.ContainsKey(Package.Version))
|
||||
{
|
||||
|
|
@ -572,14 +604,40 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
{
|
||||
var end = path.Length - extension.Length;
|
||||
var prefix = path.Substring(0, end);
|
||||
var paths = new List<string> { path };
|
||||
var paths = new List<string>
|
||||
{
|
||||
path
|
||||
};
|
||||
|
||||
var scaleFactors = new Dictionary<PackageVersion, List<int>>
|
||||
{
|
||||
// scale factors on win10: https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
|
||||
{ PackageVersion.Windows10, new List<int> { 100, 125, 150, 200, 400 } },
|
||||
{ PackageVersion.Windows81, new List<int> { 100, 120, 140, 160, 180 } },
|
||||
{ PackageVersion.Windows8, new List<int> { 100 } }
|
||||
{
|
||||
PackageVersion.Windows10, new List<int>
|
||||
{
|
||||
100,
|
||||
125,
|
||||
150,
|
||||
200,
|
||||
400
|
||||
}
|
||||
},
|
||||
{
|
||||
PackageVersion.Windows81, new List<int>
|
||||
{
|
||||
100,
|
||||
120,
|
||||
140,
|
||||
160,
|
||||
180
|
||||
}
|
||||
},
|
||||
{
|
||||
PackageVersion.Windows8, new List<int>
|
||||
{
|
||||
100
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (scaleFactors.ContainsKey(Package.Version))
|
||||
|
|
@ -598,15 +656,15 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
|
||||
$"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException());
|
||||
$"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
|
||||
$"|Unable to find extension from {uri} for {UserModelId} " +
|
||||
$"in package location {Package.Location}", new FileNotFoundException());
|
||||
$"|Unable to find extension from {uri} for {UserModelId} " +
|
||||
$"in package location {Package.Location}", new FileNotFoundException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
@ -633,8 +691,8 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|ImageFromPath|{(string.IsNullOrEmpty(path) ? "Not Avaliable" : path)}" +
|
||||
$"|Unable to get logo for {UserModelId} from {path} and" +
|
||||
$" located in {Package.Location}", new FileNotFoundException());
|
||||
$"|Unable to get logo for {UserModelId} from {path} and" +
|
||||
$" located in {Package.Location}", new FileNotFoundException());
|
||||
return new BitmapImage(new Uri(Constant.MissingImgIcon));
|
||||
}
|
||||
}
|
||||
|
|
@ -682,8 +740,8 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
else
|
||||
{
|
||||
ProgramLogger.LogException($"|UWP|PlatedImage|{Package.Location}" +
|
||||
$"|Unable to convert background string {BackgroundColor} " +
|
||||
$"to color for {Package.Location}", new InvalidOperationException());
|
||||
$"|Unable to convert background string {BackgroundColor} " +
|
||||
$"to color for {Package.Location}", new InvalidOperationException());
|
||||
|
||||
return new BitmapImage(new Uri(Constant.MissingImgIcon));
|
||||
}
|
||||
|
|
@ -701,6 +759,14 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
}
|
||||
|
||||
public static void Dispose()
|
||||
{
|
||||
foreach (var fileSystemWatcher in _watchers)
|
||||
{
|
||||
fileSystemWatcher.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public enum PackageVersion
|
||||
{
|
||||
Windows10,
|
||||
|
|
@ -728,5 +794,6 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern Hresult SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf,
|
||||
IntPtr ppvReserved);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -12,9 +12,11 @@ using Flow.Launcher.Plugin.Program.Logger;
|
|||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Program.Programs
|
||||
{
|
||||
|
|
@ -109,7 +111,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
c.SpecialKeyState.ShiftPressed &&
|
||||
!c.SpecialKeyState.AltPressed &&
|
||||
!c.SpecialKeyState.WinPressed
|
||||
);
|
||||
);
|
||||
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
|
|
@ -194,6 +196,9 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
return Name;
|
||||
}
|
||||
|
||||
public static List<FileSystemWatcher> Watchers = new List<FileSystemWatcher>();
|
||||
|
||||
|
||||
private static Win32 Win32Program(string path)
|
||||
{
|
||||
try
|
||||
|
|
@ -216,7 +221,10 @@ 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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -294,7 +302,10 @@ 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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -305,8 +316,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
|
||||
return Directory.EnumerateFiles(directory, "*", new EnumerationOptions
|
||||
{
|
||||
IgnoreInaccessible = true,
|
||||
RecurseSubdirectories = true
|
||||
IgnoreInaccessible = true, RecurseSubdirectories = true
|
||||
}).Where(x => suffixes.Contains(Extension(x)));
|
||||
}
|
||||
|
||||
|
|
@ -545,5 +555,68 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
|
||||
return UniqueIdentifier == other.UniqueIdentifier;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetStartMenuPaths()
|
||||
{
|
||||
var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
|
||||
var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonPrograms);
|
||||
return new[]
|
||||
{
|
||||
directory1, directory2
|
||||
};
|
||||
}
|
||||
|
||||
public static void WatchProgramUpdate(Settings settings)
|
||||
{
|
||||
var paths = new List<string>();
|
||||
if (settings.EnableStartMenuSource)
|
||||
paths.AddRange(GetStartMenuPaths());
|
||||
|
||||
paths.AddRange(from source in settings.ProgramSources where source.Enabled select source.Location);
|
||||
|
||||
foreach (var directory in from path in paths where Directory.Exists(path) select path)
|
||||
{
|
||||
WatchDirectory(directory);
|
||||
}
|
||||
|
||||
_ = Task.Run(MonitorDirectoryChangeAsync);
|
||||
}
|
||||
|
||||
private static Channel<byte> indexQueue = Channel.CreateBounded<byte>(1);
|
||||
|
||||
public static async Task MonitorDirectoryChangeAsync()
|
||||
{
|
||||
var reader = indexQueue.Reader;
|
||||
while (await reader.WaitToReadAsync())
|
||||
{
|
||||
await Task.Delay(500);
|
||||
while (reader.TryRead(out _))
|
||||
{
|
||||
}
|
||||
Main.IndexWin32Programs();
|
||||
}
|
||||
}
|
||||
|
||||
public static void WatchDirectory(string directory)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
throw new ArgumentException("Path Not Exist");
|
||||
}
|
||||
var watcher = new FileSystemWatcher(directory);
|
||||
|
||||
watcher.Created += static (_, _) => indexQueue.Writer.TryWrite(default);
|
||||
watcher.Deleted += static (_, _) => indexQueue.Writer.TryWrite(default);
|
||||
watcher.EnableRaisingEvents = true;
|
||||
watcher.IncludeSubdirectories = true;
|
||||
}
|
||||
|
||||
public static void Dispose()
|
||||
{
|
||||
foreach (var fileSystemWatcher in Watchers)
|
||||
{
|
||||
fileSystemWatcher.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue