Flow.Launcher/Plugins/Flow.Launcher.Plugin.Program/Main.cs

280 lines
10 KiB
C#
Raw Normal View History

using System;
2014-01-04 12:26:13 +00:00
using System.Collections.Generic;
using System.Diagnostics;
2014-01-04 12:26:13 +00:00
using System.Linq;
2021-01-02 09:58:30 +00:00
using System.Threading;
2016-11-30 01:07:48 +00:00
using System.Threading.Tasks;
2016-01-06 21:34:42 +00:00
using System.Windows.Controls;
2020-04-21 09:12:17 +00:00
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.Program.Programs;
using Flow.Launcher.Plugin.Program.Views;
2022-10-20 08:36:58 +00:00
using Flow.Launcher.Plugin.Program.Views.Models;
2021-05-27 11:28:17 +00:00
using Microsoft.Extensions.Caching.Memory;
using Path = System.IO.Path;
2020-04-21 09:12:17 +00:00
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
2014-01-04 12:26:13 +00:00
2020-04-21 09:12:17 +00:00
namespace Flow.Launcher.Plugin.Program
2014-01-04 12:26:13 +00:00
{
public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISavable, IAsyncReloadable,
IDisposable
2014-01-04 12:26:13 +00:00
{
2019-09-08 12:18:55 +00:00
internal static Win32[] _win32s { get; set; }
internal static UWPApp[] _uwps { get; set; }
2019-09-08 12:18:55 +00:00
internal static Settings _settings { get; set; }
2019-10-17 20:53:00 +00:00
2021-11-06 06:10:29 +00:00
internal static PluginInitContext Context { get; private set; }
private static BinaryStorage<Win32[]> _win32Storage;
private static BinaryStorage<UWPApp[]> _uwpStorage;
2021-05-24 02:21:39 +00:00
private static readonly List<Result> emptyResults = new();
private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 };
2021-05-27 11:28:17 +00:00
private static MemoryCache cache = new(cacheOptions);
private static readonly string[] commonUninstallerNames =
{
"uninst.exe",
"unins000.exe",
"uninst000.exe",
"uninstall.exe"
};
// For cases when the uninstaller is named like "Uninstall Program Name.exe"
private const string CommonUninstallerPrefix = "uninstall";
private const string CommonUninstallerSuffix = ".exe";
2021-05-27 11:28:17 +00:00
static Main()
{
}
public void Save()
{
_win32Storage.SaveAsync(_win32s);
_uwpStorage.SaveAsync(_uwps);
}
2021-01-02 09:58:30 +00:00
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
2014-01-04 12:26:13 +00:00
{
2021-05-27 11:28:17 +00:00
var result = await cache.GetOrCreateAsync(query.Search, async entry =>
{
var resultList = await Task.Run(() =>
{
try
{
return _win32s.Cast<IProgram>()
.Concat(_uwps)
.AsParallel()
.WithCancellation(token)
.Where(HideUninstallersFilter)
.Where(p => p.Enabled)
.Select(p => p.Result(query.Search, Context.API))
.Where(r => r?.Score > 0)
.ToList();
}
catch (OperationCanceledException)
{
2024-11-24 01:21:27 +00:00
Log.Debug("|Flow.Launcher.Plugin.Program.Main|Query operation cancelled");
return emptyResults;
}
2024-11-23 13:32:42 +00:00
}, token);
resultList = resultList.Any() ? resultList : emptyResults;
entry.SetSize(resultList.Count);
entry.SetSlidingExpiration(TimeSpan.FromHours(8));
return resultList;
});
return result;
2014-01-04 12:26:13 +00:00
}
private bool HideUninstallersFilter(IProgram program)
{
if (!_settings.HideUninstallers) return true;
if (program is not Win32 win32) return true;
var fileName = Path.GetFileName(win32.ExecutablePath);
return !commonUninstallerNames.Contains(fileName, StringComparer.OrdinalIgnoreCase) &&
!(fileName.StartsWith(CommonUninstallerPrefix, StringComparison.OrdinalIgnoreCase) &&
fileName.EndsWith(CommonUninstallerSuffix, StringComparison.OrdinalIgnoreCase));
}
2021-01-02 09:58:30 +00:00
public async Task InitAsync(PluginInitContext context)
2014-01-04 12:26:13 +00:00
{
2021-11-06 18:58:28 +00:00
Context = context;
2021-01-02 09:58:30 +00:00
_settings = context.API.LoadSettingJsonStorage<Settings>();
2021-01-02 09:58:30 +00:00
await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", async () =>
{
_win32Storage = new BinaryStorage<Win32[]>("Win32");
_win32s = await _win32Storage.TryLoadAsync(Array.Empty<Win32>());
_uwpStorage = new BinaryStorage<UWPApp[]>("UWP");
_uwps = await _uwpStorage.TryLoadAsync(Array.Empty<UWPApp>());
2021-01-02 09:58:30 +00:00
});
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>");
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>");
bool cacheEmpty = !_win32s.Any() || !_uwps.Any();
2021-01-02 09:58:30 +00:00
if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now)
2021-01-02 09:58:30 +00:00
{
_ = Task.Run(async () =>
{
await IndexProgramsAsync().ConfigureAwait(false);
WatchProgramUpdate();
});
}
else
2021-01-02 09:58:30 +00:00
{
WatchProgramUpdate();
}
2021-05-24 02:21:39 +00:00
static void WatchProgramUpdate()
{
Win32.WatchProgramUpdate(_settings);
_ = UWPPackage.WatchPackageChange();
}
}
public static void IndexWin32Programs()
{
2019-11-16 00:37:01 +00:00
var win32S = Win32.All(_settings);
2020-10-14 15:43:16 +00:00
_win32s = win32S;
ResetCache();
_win32Storage.SaveAsync(_win32s);
2023-04-24 11:50:34 +00:00
_settings.LastIndexTime = DateTime.Now;
}
2021-01-02 09:58:30 +00:00
public static void IndexUwpPrograms()
{
var applications = UWPPackage.All(_settings);
2020-10-14 15:43:16 +00:00
_uwps = applications;
ResetCache();
_uwpStorage.SaveAsync(_uwps);
2023-04-24 11:50:34 +00:00
_settings.LastIndexTime = DateTime.Now;
}
2014-03-22 05:50:20 +00:00
public static async Task IndexProgramsAsync()
{
2022-10-29 18:37:22 +00:00
var a = Task.Run(() =>
{
Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs);
});
var b = Task.Run(() =>
{
Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|UWPProgram index cost", IndexUwpPrograms);
});
await Task.WhenAll(a, b).ConfigureAwait(false);
}
internal static void ResetCache()
{
2021-05-27 11:28:17 +00:00
var oldCache = cache;
cache = new MemoryCache(cacheOptions);
oldCache.Dispose();
2019-11-28 23:38:50 +00:00
}
2019-09-07 05:58:13 +00:00
2016-01-06 21:34:42 +00:00
public Control CreateSettingPanel()
{
2021-11-06 18:58:28 +00:00
return new ProgramSetting(Context, _settings, _win32s, _uwps);
}
2015-02-07 12:17:49 +00:00
public string GetTranslatedPluginTitle()
{
2021-11-06 18:58:28 +00:00
return Context.API.GetTranslation("flowlauncher_plugin_program_plugin_name");
2015-02-07 12:17:49 +00:00
}
public string GetTranslatedPluginDescription()
{
2021-11-06 18:58:28 +00:00
return Context.API.GetTranslation("flowlauncher_plugin_program_plugin_description");
2015-02-07 12:17:49 +00:00
}
public List<Result> LoadContextMenus(Result selectedResult)
{
var menuOptions = new List<Result>();
var program = selectedResult.ContextData as IProgram;
if (program != null)
{
2021-11-06 18:58:28 +00:00
menuOptions = program.ContextMenus(Context.API);
}
menuOptions.Add(
2021-01-02 09:58:30 +00:00
new Result
{
2021-11-06 18:58:28 +00:00
Title = Context.API.GetTranslation("flowlauncher_plugin_program_disable_program"),
2021-01-02 09:58:30 +00:00
Action = c =>
{
DisableProgram(program);
2021-11-06 18:58:28 +00:00
Context.API.ShowMsg(
Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"),
Context.API.GetTranslation(
2021-01-02 09:58:30 +00:00
"flowlauncher_plugin_program_disable_dlgtitle_success_message"));
return false;
},
2021-10-07 10:58:54 +00:00
IcoPath = "Images/disable.png",
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xece4"),
2021-01-02 09:58:30 +00:00
}
);
return menuOptions;
}
2022-10-21 10:39:03 +00:00
private static void DisableProgram(IProgram programToDelete)
{
if (_settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
return;
if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
{
2022-10-28 10:06:44 +00:00
var program = _uwps.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
program.Enabled = false;
_settings.DisabledProgramSources.Add(new ProgramSource(program));
2022-10-28 08:44:44 +00:00
_ = Task.Run(() =>
2022-10-25 06:41:46 +00:00
{
IndexUwpPrograms();
2022-10-25 06:41:46 +00:00
});
}
2022-10-25 06:41:46 +00:00
else if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
{
2022-10-28 10:06:44 +00:00
var program = _win32s.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
program.Enabled = false;
_settings.DisabledProgramSources.Add(new ProgramSource(program));
2022-10-28 08:44:44 +00:00
_ = Task.Run(() =>
2022-10-25 06:41:46 +00:00
{
IndexWin32Programs();
2022-10-25 06:41:46 +00:00
});
}
}
public static void StartProcess(Func<ProcessStartInfo, Process> runProcess, ProcessStartInfo info)
{
try
{
runProcess(info);
}
catch (Exception)
{
2022-11-29 10:13:55 +00:00
var title = Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_error");
var message = string.Format(Context.API.GetTranslation("flowlauncher_plugin_program_run_failed"),
info.FileName);
2022-11-29 10:13:55 +00:00
Context.API.ShowMsg(title, string.Format(message, info.FileName), string.Empty);
}
}
2019-10-06 02:15:06 +00:00
2021-01-02 09:58:30 +00:00
public async Task ReloadDataAsync()
2019-10-06 02:15:06 +00:00
{
await IndexProgramsAsync();
2019-10-06 02:15:06 +00:00
}
2022-10-21 10:39:03 +00:00
public void Dispose()
{
Win32.Dispose();
}
2014-01-04 12:26:13 +00:00
}
}