Merge branch 'dev' into 250412-ImportThemePreset

This commit is contained in:
DB P 2025-04-14 09:09:04 +09:00 committed by GitHub
commit b2a3782f63
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 285 additions and 301 deletions

View file

@ -1,21 +1,22 @@
using Microsoft.Win32;
using Squirrel;
using System;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.SharedCommands;
using System.Linq;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Microsoft.Win32;
using Squirrel;
namespace Flow.Launcher.Core.Configuration
{
public class Portable : IPortable
{
private static readonly string ClassName = nameof(Portable);
private readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
/// <summary>
@ -51,7 +52,7 @@ namespace Flow.Launcher.Core.Configuration
}
catch (Exception e)
{
Log.Exception("|Portable.DisablePortableMode|Error occurred while disabling portable mode", e);
API.LogException(ClassName, "Error occurred while disabling portable mode", e);
}
}
@ -75,7 +76,7 @@ namespace Flow.Launcher.Core.Configuration
}
catch (Exception e)
{
Log.Exception("|Portable.EnablePortableMode|Error occurred while enabling portable mode", e);
API.LogException(ClassName, "Error occurred while enabling portable mode", e);
}
}

View file

@ -1,25 +1,32 @@
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin;
using System;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Net.Sockets;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.ExternalPlugins
{
public record CommunityPluginSource(string ManifestFileUrl)
{
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private static readonly string ClassName = nameof(CommunityPluginSource);
private string latestEtag = "";
private List<UserPlugin> plugins = new();
private static JsonSerializerOptions PluginStoreItemSerializationOption = new JsonSerializerOptions()
private static readonly JsonSerializerOptions PluginStoreItemSerializationOption = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
};
@ -34,35 +41,49 @@ namespace Flow.Launcher.Core.ExternalPlugins
/// </remarks>
public async Task<List<UserPlugin>> FetchAsync(CancellationToken token)
{
Log.Info(nameof(CommunityPluginSource), $"Loading plugins from {ManifestFileUrl}");
API.LogInfo(ClassName, $"Loading plugins from {ManifestFileUrl}");
var request = new HttpRequestMessage(HttpMethod.Get, ManifestFileUrl);
request.Headers.Add("If-None-Match", latestEtag);
using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token)
try
{
using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token)
.ConfigureAwait(false);
if (response.StatusCode == HttpStatusCode.OK)
{
this.plugins = await response.Content
.ReadFromJsonAsync<List<UserPlugin>>(PluginStoreItemSerializationOption, cancellationToken: token)
.ConfigureAwait(false);
this.latestEtag = response.Headers.ETag?.Tag;
if (response.StatusCode == HttpStatusCode.OK)
{
plugins = await response.Content
.ReadFromJsonAsync<List<UserPlugin>>(PluginStoreItemSerializationOption, cancellationToken: token)
.ConfigureAwait(false);
latestEtag = response.Headers.ETag?.Tag;
Log.Info(nameof(CommunityPluginSource), $"Loaded {this.plugins.Count} plugins from {ManifestFileUrl}");
return this.plugins;
API.LogInfo(ClassName, $"Loaded {plugins.Count} plugins from {ManifestFileUrl}");
return plugins;
}
else if (response.StatusCode == HttpStatusCode.NotModified)
{
API.LogInfo(ClassName, $"Resource {ManifestFileUrl} has not been modified.");
return plugins;
}
else
{
API.LogWarn(ClassName, $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
return plugins;
}
}
else if (response.StatusCode == HttpStatusCode.NotModified)
catch (Exception e)
{
Log.Info(nameof(CommunityPluginSource), $"Resource {ManifestFileUrl} has not been modified.");
return this.plugins;
}
else
{
Log.Warn(nameof(CommunityPluginSource),
$"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
throw new Exception($"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
{
API.LogException(ClassName, $"Check your connection and proxy settings to {ManifestFileUrl}.", e);
}
else
{
API.LogException(ClassName, "Error Occurred", e);
}
return plugins;
}
}
}

View file

@ -5,7 +5,6 @@ using System.Linq;
using System.Windows;
using System.Windows.Forms;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
@ -14,6 +13,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
public abstract class AbstractPluginEnvironment
{
private static readonly string ClassName = nameof(AbstractPluginEnvironment);
protected readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
internal abstract string Language { get; }
@ -120,7 +121,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
else
{
API.ShowMsgBox(string.Format(API.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
Log.Error("PluginsLoader",
API.LogError(ClassName,
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
$"{Language}Environment");

View file

@ -4,7 +4,6 @@ using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
internal class JavaScriptEnvironment : TypeScriptEnvironment
{
internal override string Language => AllowedLanguage.JavaScript;

View file

@ -4,7 +4,6 @@ using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
internal class JavaScriptV2Environment : TypeScriptV2Environment
{
internal override string Language => AllowedLanguage.JavaScriptV2;

View file

@ -5,6 +5,7 @@ using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Microsoft.VisualStudio.Threading;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
@ -30,13 +31,15 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal PythonEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
internal override void InstallEnvironment()
{
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
// Python 3.11.4 is no longer Windows 7 compatible. If user is on Win 7 and
// uses Python plugin they need to custom install and use v3.8.9
DroplexPackage.Drop(App.python_3_11_4_embeddable, InstallPath).Wait();
JTF.Run(() => DroplexPackage.Drop(App.python_3_11_4_embeddable, InstallPath));
PluginsSettingsFilePath = ExecutablePath;
}

View file

@ -5,6 +5,7 @@ using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Microsoft.VisualStudio.Threading;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
@ -27,11 +28,13 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal TypeScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
internal override void InstallEnvironment()
{
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();
JTF.Run(() => DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath));
PluginsSettingsFilePath = ExecutablePath;
}

View file

@ -5,6 +5,7 @@ using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Microsoft.VisualStudio.Threading;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
@ -27,11 +28,13 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal TypeScriptV2Environment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
internal override void InstallEnvironment()
{
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();
JTF.Run(() => DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath));
PluginsSettingsFilePath = ExecutablePath;
}

View file

@ -9,6 +9,8 @@ namespace Flow.Launcher.Core.ExternalPlugins
{
public static class PluginsManifest
{
private static readonly string ClassName = nameof(PluginsManifest);
private static readonly CommunityPluginStore mainPluginStore =
new("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/plugin_api_v2/plugins.json",
"https://fastly.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@plugin_api_v2/plugins.json",
@ -44,7 +46,7 @@ namespace Flow.Launcher.Core.ExternalPlugins
}
catch (Exception e)
{
Ioc.Default.GetRequiredService<IPublicAPI>().LogException(nameof(PluginsManifest), "Http request failed", e);
Ioc.Default.GetRequiredService<IPublicAPI>().LogException(ClassName, "Http request failed", e);
}
finally
{

View file

@ -3,14 +3,20 @@ using System.Collections.Generic;
using System.Linq;
using System.IO;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin;
using System.Text.Json;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Core.Plugin
{
internal abstract class PluginConfig
{
private static readonly string ClassName = nameof(PluginConfig);
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
/// <summary>
/// Parse plugin metadata in the given directories
/// </summary>
@ -32,7 +38,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
Log.Exception($"|PluginConfig.ParsePLuginConfigs|Can't delete <{directory}>", e);
API.LogException(ClassName, $"Can't delete <{directory}>", e);
}
}
else
@ -49,11 +55,11 @@ namespace Flow.Launcher.Core.Plugin
duplicateList
.ForEach(
x => Log.Warn("PluginConfig",
string.Format("Duplicate plugin name: {0}, id: {1}, version: {2} " +
"not loaded due to version not the highest of the duplicates",
x.Name, x.ID, x.Version),
"GetUniqueLatestPluginMetadata"));
x => API.LogWarn(ClassName,
string.Format("Duplicate plugin name: {0}, id: {1}, version: {2} " +
"not loaded due to version not the highest of the duplicates",
x.Name, x.ID, x.Version),
"GetUniqueLatestPluginMetadata"));
return uniqueList;
}
@ -101,7 +107,7 @@ namespace Flow.Launcher.Core.Plugin
string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
if (!File.Exists(configPath))
{
Log.Error($"|PluginConfig.GetPluginMetadata|Didn't find config file <{configPath}>");
API.LogError(ClassName, $"Didn't find config file <{configPath}>");
return null;
}
@ -117,19 +123,19 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
Log.Exception($"|PluginConfig.GetPluginMetadata|invalid json for config <{configPath}>", e);
API.LogException(ClassName, $"Invalid json for config <{configPath}>", e);
return null;
}
if (!AllowedLanguage.IsAllowed(metadata.Language))
{
Log.Error($"|PluginConfig.GetPluginMetadata|Invalid language <{metadata.Language}> for config <{configPath}>");
API.LogError(ClassName, $"Invalid language <{metadata.Language}> for config <{configPath}>");
return null;
}
if (!File.Exists(metadata.ExecuteFilePath))
{
Log.Error($"|PluginConfig.GetPluginMetadata|execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}");
API.LogError(ClassName, $"Execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}");
return null;
}

View file

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.ExternalPlugins;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
@ -214,12 +213,12 @@ namespace Flow.Launcher.Core.Plugin
() => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, API)));
pair.Metadata.InitTime += milliseconds;
Log.Info(
$"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
API.LogInfo(ClassName,
$"Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
}
catch (Exception e)
{
Log.Exception(ClassName, $"Fail to Init plugin: {pair.Metadata.Name}", e);
API.LogException(ClassName, $"Fail to Init plugin: {pair.Metadata.Name}", e);
pair.Metadata.Disabled = true;
failedPlugins.Enqueue(pair);
}
@ -370,8 +369,8 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
Log.Exception(
$"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>",
API.LogException(ClassName,
$"Can't load context menus for plugin <{pluginPair.Metadata.Name}>",
e);
}
}
@ -563,7 +562,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
Log.Exception($"|PluginManager.InstallPlugin|Failed to delete temp folder {tempFolderPluginPath}", e);
API.LogException(ClassName, $"Failed to delete temp folder {tempFolderPluginPath}", e);
}
if (checkModified)
@ -608,7 +607,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin settings folder for {plugin.Name}", e);
API.LogException(ClassName, $"Failed to delete plugin settings folder for {plugin.Name}", e);
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"),
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name));
}
@ -624,7 +623,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin cache folder for {plugin.Name}", e);
API.LogException(ClassName, $"Failed to delete plugin cache folder for {plugin.Name}", e);
API.ShowMsg(API.GetTranslation("failedToRemovePluginCacheTitle"),
string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name));
}

View file

@ -89,19 +89,19 @@ namespace Flow.Launcher.Core.Plugin
#else
catch (Exception e) when (assembly == null)
{
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
Log.Exception(ClassName, $"Couldn't load assembly for the plugin: {metadata.Name}", e);
}
catch (InvalidOperationException e)
{
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
Log.Exception(ClassName, $"Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
}
catch (ReflectionTypeLoadException e)
{
Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
Log.Exception(ClassName, $"The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
}
catch (Exception e)
{
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
Log.Exception(ClassName, $"The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
}
#endif

View file

@ -1,21 +1,19 @@
#nullable enable
using System;
using System.Collections.Generic;
using System;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Threading.Tasks;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin;
using Meziantou.Framework.Win32;
using Microsoft.VisualBasic.ApplicationServices;
using Nerdbank.Streams;
#nullable enable
namespace Flow.Launcher.Core.Plugin
{
internal abstract class ProcessStreamPluginV2 : JsonRPCPluginV2
{
private static JobObject _jobObject = new JobObject();
private static readonly JobObject _jobObject = new();
static ProcessStreamPluginV2()
{
@ -66,11 +64,10 @@ namespace Flow.Launcher.Core.Plugin
ClientPipe = new DuplexPipe(reader, writer);
}
public override async Task ReloadDataAsync()
{
var oldProcess = ClientProcess;
ClientProcess = Process.Start(StartInfo);
ClientProcess = Process.Start(StartInfo)!;
ArgumentNullException.ThrowIfNull(ClientProcess);
SetupPipe(ClientProcess);
await base.ReloadDataAsync();
@ -79,7 +76,6 @@ namespace Flow.Launcher.Core.Plugin
oldProcess.Dispose();
}
public override async ValueTask DisposeAsync()
{
await base.DisposeAsync();

View file

@ -6,7 +6,6 @@ using System.Reflection;
using System.Windows;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using System.Globalization;
@ -17,6 +16,12 @@ namespace Flow.Launcher.Core.Resource
{
public class Internationalization
{
private static readonly string ClassName = nameof(Internationalization);
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private const string Folder = "Languages";
private const string DefaultLanguageCode = "en";
private const string DefaultFile = "en.xaml";
@ -80,7 +85,7 @@ namespace Flow.Launcher.Core.Resource
}
else
{
Log.Error($"|Internationalization.AddPluginLanguageDirectories|Can't find plugin path <{location}> for <{plugin.Metadata.Name}>");
API.LogError(ClassName, $"Can't find plugin path <{location}> for <{plugin.Metadata.Name}>");
}
}
@ -150,7 +155,7 @@ namespace Flow.Launcher.Core.Resource
var language = AvailableLanguages.GetAvailableLanguages().FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase);
if (language == null)
{
Log.Error($"|Internationalization.GetLanguageByLanguageCode|Language code can't be found <{languageCode}>");
API.LogError(ClassName, $"Language code can't be found <{languageCode}>");
return AvailableLanguages.English;
}
else
@ -248,7 +253,7 @@ namespace Flow.Launcher.Core.Resource
}
else
{
Log.Error($"|Internationalization.GetTranslation|No Translation for key {key}");
API.LogError(ClassName, $"No Translation for key {key}");
return $"No Translation for key {key}";
}
}
@ -266,7 +271,7 @@ namespace Flow.Launcher.Core.Resource
}
catch (Exception e)
{
Log.Exception($"|Internationalization.UpdatePluginMetadataTranslations|Failed for <{p.Metadata.Name}>", e);
API.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e);
}
}
}
@ -282,7 +287,7 @@ namespace Flow.Launcher.Core.Resource
}
else
{
Log.Error($"|Internationalization.LanguageFile|Language path can't be found <{path}>");
API.LogError(ClassName, $"Language path can't be found <{path}>");
var english = Path.Combine(folder, DefaultFile);
if (File.Exists(english))
{
@ -290,7 +295,7 @@ namespace Flow.Launcher.Core.Resource
}
else
{
Log.Error($"|Internationalization.LanguageFile|Default English Language path can't be found <{path}>");
API.LogError(ClassName, $"Default English Language path can't be found <{path}>");
return string.Empty;
}
}

View file

@ -13,7 +13,6 @@ using System.Windows.Media.Effects;
using System.Windows.Shell;
using System.Windows.Threading;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
@ -25,6 +24,8 @@ namespace Flow.Launcher.Core.Resource
{
#region Properties & Fields
private readonly string ClassName = nameof(Theme);
public bool BlurEnabled { get; private set; }
private const string ThemeMetadataNamePrefix = "Name:";
@ -73,7 +74,7 @@ namespace Flow.Launcher.Core.Resource
}
else
{
Log.Error("Current theme resource not found. Initializing with default theme.");
_api.LogError(ClassName, "Current theme resource not found. Initializing with default theme.");
_oldTheme = Constant.DefaultTheme;
};
}
@ -92,7 +93,7 @@ namespace Flow.Launcher.Core.Resource
}
catch (Exception e)
{
Log.Exception($"|Theme.MakesureThemeDirectoriesExist|Exception when create directory <{dir}>", e);
_api.LogException(ClassName, $"Exception when create directory <{dir}>", e);
}
}
}
@ -135,7 +136,7 @@ namespace Flow.Launcher.Core.Resource
}
catch (Exception e)
{
Log.Exception("Error occurred while updating theme fonts", e);
_api.LogException(ClassName, "Error occurred while updating theme fonts", e);
}
}
@ -387,7 +388,7 @@ namespace Flow.Launcher.Core.Resource
var matchingTheme = themes.FirstOrDefault(t => t.FileNameWithoutExtension == _settings.Theme);
if (matchingTheme == null)
{
Log.Warn($"No matching theme found for '{_settings.Theme}'. Falling back to the first available theme.");
_api.LogWarn(ClassName, $"No matching theme found for '{_settings.Theme}'. Falling back to the first available theme.");
}
return matchingTheme ?? themes.FirstOrDefault();
}
@ -440,7 +441,7 @@ namespace Flow.Launcher.Core.Resource
}
catch (DirectoryNotFoundException)
{
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found");
_api.LogError(ClassName, $"Theme <{theme}> path can't be found");
if (theme != Constant.DefaultTheme)
{
_api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_path_not_exists"), theme));
@ -450,7 +451,7 @@ namespace Flow.Launcher.Core.Resource
}
catch (XamlParseException)
{
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse");
_api.LogError(ClassName, $"Theme <{theme}> fail to parse");
if (theme != Constant.DefaultTheme)
{
_api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_parse_error"), theme));

View file

@ -12,7 +12,6 @@ using System.Windows;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using JetBrains.Annotations;
@ -24,6 +23,8 @@ namespace Flow.Launcher.Core
{
public string GitHubRepository { get; init; }
private static readonly string ClassName = nameof(Updater);
private readonly IPublicAPI _api;
public Updater(IPublicAPI publicAPI, string gitHubRepository)
@ -51,7 +52,7 @@ namespace Flow.Launcher.Core
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
var currentVersion = Version.Parse(Constant.Version);
Log.Info($"|Updater.UpdateApp|Future Release <{Formatted(newUpdateInfo.FutureReleaseEntry)}>");
_api.LogInfo(ClassName, $"Future Release <{Formatted(newUpdateInfo.FutureReleaseEntry)}>");
if (newReleaseVersion <= currentVersion)
{
@ -84,7 +85,7 @@ namespace Flow.Launcher.Core
var newVersionTips = NewVersionTips(newReleaseVersion.ToString());
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
_api.LogInfo(ClassName, $"Update success:{newVersionTips}");
if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
@ -93,10 +94,14 @@ namespace Flow.Launcher.Core
}
catch (Exception e)
{
if ((e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException))
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
{
_api.LogException(ClassName, $"Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
}
else
Log.Exception($"|Updater.UpdateApp|Error Occurred", e);
{
_api.LogException(ClassName, $"Error Occurred", e);
}
if (!silentUpdate)
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_fail"),

View file

@ -1,30 +1,31 @@
using System.IO;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using System;
using System.Threading;
using Flow.Launcher.Plugin;
using CommunityToolkit.Mvvm.DependencyInjection;
using JetBrains.Annotations;
namespace Flow.Launcher.Infrastructure.Http
{
public static class Http
{
private static readonly string ClassName = nameof(Http);
private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko";
private static HttpClient client = new HttpClient();
private static readonly HttpClient client = new();
static Http()
{
// need to be added so it would work on a win10 machine
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
client.DefaultRequestHeaders.Add("User-Agent", UserAgent);
HttpClient.DefaultProxy = WebProxy;
@ -34,7 +35,7 @@ namespace Flow.Launcher.Infrastructure.Http
public static HttpProxy Proxy
{
private get { return proxy; }
private get => proxy;
set
{
proxy = value;
@ -72,13 +73,13 @@ namespace Flow.Launcher.Infrastructure.Http
ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
_ => throw new ArgumentOutOfRangeException()
_ => throw new ArgumentOutOfRangeException(null)
};
}
catch (UriFormatException e)
{
Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsg("Please try again", "Unable to parse Http Proxy");
Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e);
Log.Exception(ClassName, "Unable to parse Uri", e);
}
}
@ -134,7 +135,7 @@ namespace Flow.Launcher.Infrastructure.Http
}
catch (HttpRequestException e)
{
Log.Exception("Infrastructure.Http", "Http Request Error", e, "DownloadAsync");
Log.Exception(ClassName, "Http Request Error", e, "DownloadAsync");
throw;
}
}
@ -147,7 +148,7 @@ namespace Flow.Launcher.Infrastructure.Http
/// <returns>The Http result as string. Null if cancellation requested</returns>
public static Task<string> GetAsync([NotNull] string url, CancellationToken token = default)
{
Log.Debug($"|Http.Get|Url <{url}>");
Log.Debug(ClassName, $"Url <{url}>");
return GetAsync(new Uri(url), token);
}
@ -159,7 +160,7 @@ namespace Flow.Launcher.Infrastructure.Http
/// <returns>The Http result as string. Null if cancellation requested</returns>
public static async Task<string> GetAsync([NotNull] Uri url, CancellationToken token = default)
{
Log.Debug($"|Http.Get|Url <{url}>");
Log.Debug(ClassName, $"Url <{url}>");
using var response = await client.GetAsync(url, token);
var content = await response.Content.ReadAsStringAsync(token);
if (response.StatusCode != HttpStatusCode.OK)
@ -191,7 +192,7 @@ namespace Flow.Launcher.Infrastructure.Http
public static async Task<Stream> GetStreamAsync([NotNull] Uri url,
CancellationToken token = default)
{
Log.Debug($"|Http.Get|Url <{url}>");
Log.Debug(ClassName, $"Url <{url}>");
return await client.GetStreamAsync(url, token);
}
@ -202,7 +203,7 @@ namespace Flow.Launcher.Infrastructure.Http
public static async Task<HttpResponseMessage> GetResponseAsync([NotNull] Uri url, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
CancellationToken token = default)
{
Log.Debug($"|Http.Get|Url <{url}>");
Log.Debug(ClassName, $"Url <{url}>");
return await client.GetAsync(url, completionOption, token);
}

View file

@ -7,9 +7,8 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin;
using SharpVectors.Converters;
using SharpVectors.Renderers.Wpf;
@ -17,10 +16,6 @@ namespace Flow.Launcher.Infrastructure.Image
{
public static class ImageLoader
{
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private static readonly string ClassName = nameof(ImageLoader);
private static readonly ImageCache ImageCache = new();
@ -58,14 +53,14 @@ namespace Flow.Launcher.Infrastructure.Image
_ = Task.Run(async () =>
{
await API.StopwatchLogInfoAsync(ClassName, "Preload images cost", async () =>
await Stopwatch.InfoAsync(ClassName, "Preload images cost", async () =>
{
foreach (var (path, isFullImage) in usage)
{
await LoadAsync(path, isFullImage);
}
});
API.LogInfo(ClassName, $"Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
Log.Info(ClassName, $"Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
});
}
@ -81,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Image
}
catch (System.Exception e)
{
API.LogException(ClassName, "Failed to save image cache to file", e);
Log.Exception(ClassName, "Failed to save image cache to file", e);
}
finally
{
@ -176,8 +171,8 @@ namespace Flow.Launcher.Infrastructure.Image
}
catch (System.Exception e2)
{
API.LogException(ClassName, $"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e);
API.LogException(ClassName, $"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2);
Log.Exception(ClassName, $"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e);
Log.Exception(ClassName, $"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2);
ImageSource image = ImageCache[Constant.MissingImgIcon, false];
ImageCache[path, false] = image;
@ -243,7 +238,7 @@ namespace Flow.Launcher.Infrastructure.Image
{
image = Image;
type = ImageType.Error;
API.LogException(ClassName, $"Failed to load image file from path {path}: {ex.Message}", ex);
Log.Exception(ClassName, $"Failed to load image file from path {path}: {ex.Message}", ex);
}
}
else
@ -267,7 +262,7 @@ namespace Flow.Launcher.Infrastructure.Image
{
image = Image;
type = ImageType.Error;
API.LogException(ClassName, $"Failed to load SVG image from path {path}: {ex.Message}", ex);
Log.Exception(ClassName, $"Failed to load SVG image from path {path}: {ex.Message}", ex);
}
}
else

View file

@ -94,13 +94,6 @@ namespace Flow.Launcher.Infrastructure.Logger
logger.Fatal(message);
}
private static bool FormatValid(string message)
{
var parts = message.Split('|');
var valid = parts.Length == 3 && !string.IsNullOrWhiteSpace(parts[1]) && !string.IsNullOrWhiteSpace(parts[2]);
return valid;
}
public static void Exception(string className, string message, System.Exception exception, [CallerMemberName] string methodName = "")
{
exception = exception.Demystify();
@ -135,57 +128,14 @@ namespace Flow.Launcher.Infrastructure.Logger
return className;
}
#if !DEBUG
private static void ExceptionInternal(string classAndMethod, string message, System.Exception e)
{
var logger = LogManager.GetLogger(classAndMethod);
logger.Error(e, message);
}
private static void LogInternal(string message, LogLevel level)
{
if (FormatValid(message))
{
var parts = message.Split('|');
var prefix = parts[1];
var unprefixed = parts[2];
var logger = LogManager.GetLogger(prefix);
logger.Log(level, unprefixed);
}
else
{
LogFaultyFormat(message);
}
}
/// Example: "|ClassName.MethodName|Message"
/// <param name="message">Example: "|ClassName.MethodName|Message" </param>
/// <param name="e">Exception</param>
public static void Exception(string message, System.Exception e)
{
e = e.Demystify();
#if DEBUG
ExceptionDispatchInfo.Capture(e).Throw();
#else
if (FormatValid(message))
{
var parts = message.Split('|');
var prefix = parts[1];
var unprefixed = parts[2];
ExceptionInternal(prefix, unprefixed, e);
}
else
{
LogFaultyFormat(message);
}
#endif
}
/// Example: "|ClassName.MethodName|Message"
public static void Error(string message)
{
LogInternal(message, LogLevel.Error);
}
public static void Error(string className, string message, [CallerMemberName] string methodName = "")
{
@ -206,33 +156,15 @@ namespace Flow.Launcher.Infrastructure.Logger
LogInternal(LogLevel.Debug, className, message, methodName);
}
/// Example: "|ClassName.MethodName|Message""
public static void Debug(string message)
{
LogInternal(message, LogLevel.Debug);
}
public static void Info(string className, string message, [CallerMemberName] string methodName = "")
{
LogInternal(LogLevel.Info, className, message, methodName);
}
/// Example: "|ClassName.MethodName|Message"
public static void Info(string message)
{
LogInternal(message, LogLevel.Info);
}
public static void Warn(string className, string message, [CallerMemberName] string methodName = "")
{
LogInternal(LogLevel.Warn, className, message, methodName);
}
/// Example: "|ClassName.MethodName|Message"
public static void Warn(string message)
{
LogInternal(message, LogLevel.Warn);
}
}
public enum LOGLEVEL

View file

@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Flow.Launcher.Infrastructure.Logger;
@ -9,54 +10,50 @@ namespace Flow.Launcher.Infrastructure
/// <summary>
/// This stopwatch will appear only in Debug mode
/// </summary>
public static long Debug(string message, Action action)
public static long Debug(string className, string message, Action action, [CallerMemberName] string methodName = "")
{
var stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start();
action();
stopWatch.Stop();
var milliseconds = stopWatch.ElapsedMilliseconds;
string info = $"{message} <{milliseconds}ms>";
Log.Debug(info);
Log.Debug(className, $"{message} <{milliseconds}ms>", methodName);
return milliseconds;
}
/// <summary>
/// This stopwatch will appear only in Debug mode
/// </summary>
public static async Task<long> DebugAsync(string message, Func<Task> action)
public static async Task<long> DebugAsync(string className, string message, Func<Task> action, [CallerMemberName] string methodName = "")
{
var stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start();
await action();
stopWatch.Stop();
var milliseconds = stopWatch.ElapsedMilliseconds;
string info = $"{message} <{milliseconds}ms>";
Log.Debug(info);
Log.Debug(className, $"{message} <{milliseconds}ms>", methodName);
return milliseconds;
}
public static long Normal(string message, Action action)
public static long Info(string className, string message, Action action, [CallerMemberName] string methodName = "")
{
var stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start();
action();
stopWatch.Stop();
var milliseconds = stopWatch.ElapsedMilliseconds;
string info = $"{message} <{milliseconds}ms>";
Log.Info(info);
Log.Info(className, $"{message} <{milliseconds}ms>", methodName);
return milliseconds;
}
public static async Task<long> NormalAsync(string message, Func<Task> action)
public static async Task<long> InfoAsync(string className, string message, Func<Task> action, [CallerMemberName] string methodName = "")
{
var stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start();
await action();
stopWatch.Stop();
var milliseconds = stopWatch.ElapsedMilliseconds;
string info = $"{message} <{milliseconds}ms>";
Log.Info(info);
Log.Info(className, $"{message} <{milliseconds}ms>", methodName);
return milliseconds;
}
}

View file

@ -20,6 +20,8 @@ namespace Flow.Launcher.Infrastructure.Storage
/// </remarks>
public class BinaryStorage<T> : ISavable
{
private static readonly string ClassName = "BinaryStorage";
protected T? Data;
public const string FileSuffix = ".cache";
@ -59,7 +61,7 @@ namespace Flow.Launcher.Infrastructure.Storage
{
if (new FileInfo(FilePath).Length == 0)
{
Log.Error($"|BinaryStorage.TryLoad|Zero length cache file <{FilePath}>");
Log.Error(ClassName, $"Zero length cache file <{FilePath}>");
Data = defaultData;
await SaveAsync();
}
@ -69,7 +71,7 @@ namespace Flow.Launcher.Infrastructure.Storage
}
else
{
Log.Info("|BinaryStorage.TryLoad|Cache file not exist, load default data");
Log.Info(ClassName, "Cache file not exist, load default data");
Data = defaultData;
await SaveAsync();
}

View file

@ -1,8 +1,7 @@
using System.IO;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Infrastructure.Storage
@ -11,10 +10,6 @@ namespace Flow.Launcher.Infrastructure.Storage
{
private static readonly string ClassName = "FlowLauncherJsonStorage";
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
public FlowLauncherJsonStorage()
{
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
@ -32,7 +27,7 @@ namespace Flow.Launcher.Infrastructure.Storage
}
catch (System.Exception e)
{
API.LogException(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
Log.Exception(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
}
}
@ -44,7 +39,7 @@ namespace Flow.Launcher.Infrastructure.Storage
}
catch (System.Exception e)
{
API.LogException(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
Log.Exception(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
}
}
}

View file

@ -16,6 +16,8 @@ namespace Flow.Launcher.Infrastructure.Storage
/// </summary>
public class JsonStorage<T> : ISavable where T : new()
{
private static readonly string ClassName = "JsonStorage";
protected T? Data;
// need a new directory name
@ -104,7 +106,7 @@ namespace Flow.Launcher.Infrastructure.Storage
private void RestoreBackup()
{
Log.Info($"|JsonStorage.Load|Failed to load settings.json, {BackupFilePath} restored successfully");
Log.Info(ClassName, $"Failed to load settings.json, {BackupFilePath} restored successfully");
if (File.Exists(FilePath))
File.Replace(BackupFilePath, FilePath, null);

View file

@ -1,7 +1,6 @@
using System.IO;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Plugin;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Infrastructure.Storage
@ -10,10 +9,6 @@ namespace Flow.Launcher.Infrastructure.Storage
{
private static readonly string ClassName = "PluginBinaryStorage";
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
public PluginBinaryStorage(string cacheName, string cacheDirectory)
{
DirectoryPath = cacheDirectory;
@ -30,7 +25,7 @@ namespace Flow.Launcher.Infrastructure.Storage
}
catch (System.Exception e)
{
API.LogException(ClassName, $"Failed to save plugin caches to path: {FilePath}", e);
Log.Exception(ClassName, $"Failed to save plugin caches to path: {FilePath}", e);
}
}
@ -42,7 +37,7 @@ namespace Flow.Launcher.Infrastructure.Storage
}
catch (System.Exception e)
{
API.LogException(ClassName, $"Failed to save plugin caches to path: {FilePath}", e);
Log.Exception(ClassName, $"Failed to save plugin caches to path: {FilePath}", e);
}
}
}

View file

@ -1,8 +1,7 @@
using System.IO;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Infrastructure.Storage
@ -14,10 +13,6 @@ namespace Flow.Launcher.Infrastructure.Storage
private static readonly string ClassName = "PluginJsonStorage";
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
public PluginJsonStorage()
{
// C# related, add python related below
@ -42,7 +37,7 @@ namespace Flow.Launcher.Infrastructure.Storage
}
catch (System.Exception e)
{
API.LogException(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
Log.Exception(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
}
}
@ -54,7 +49,7 @@ namespace Flow.Launcher.Infrastructure.Storage
}
catch (System.Exception e)
{
API.LogException(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
Log.Exception(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
}
}
}

View file

@ -376,7 +376,8 @@ namespace Flow.Launcher.Infrastructure
if (!IsForegroundWindow(hwnd))
{
var result = PInvoke.SetForegroundWindow(hwnd);
if (!result) throw new Win32Exception(Marshal.GetLastWin32Error());
// If we cannot set the foreground window, we can use the foreground window and switch the layout
if (!result) hwnd = PInvoke.GetForegroundWindow();
}
// Get the current foreground window thread ID

View file

@ -148,8 +148,8 @@ namespace Flow.Launcher
Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();
Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------");
Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}");
API.LogInfo(ClassName, "Begin Flow Launcher startup ----------------------------------------------------");
API.LogInfo(ClassName, "Runtime info:{ErrorReporting.RuntimeInfo()}");
RegisterAppDomainExceptions();
RegisterDispatcherUnhandledException();
@ -176,7 +176,7 @@ namespace Flow.Launcher
_mainWindow = new MainWindow();
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
API.LogInfo(ClassName, "Dependencies Info:{ErrorReporting.DependenciesInfo()}");
Current.MainWindow = _mainWindow;
Current.MainWindow.Title = Constant.FlowLauncher;
@ -192,7 +192,7 @@ namespace Flow.Launcher
AutoUpdates();
API.SaveAppAllSettings();
Log.Info("|App.OnStartup|End Flow Launcher startup ----------------------------------------------------");
API.LogInfo(ClassName, "End Flow Launcher startup ----------------------------------------------------");
});
}
@ -250,19 +250,19 @@ namespace Flow.Launcher
{
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
Log.Info("|App.RegisterExitEvents|Process Exit");
API.LogInfo(ClassName, "Process Exit");
Dispose();
};
Current.Exit += (s, e) =>
{
Log.Info("|App.RegisterExitEvents|Application Exit");
API.LogInfo(ClassName, "Application Exit");
Dispose();
};
Current.SessionEnding += (s, e) =>
{
Log.Info("|App.RegisterExitEvents|Session Ending");
API.LogInfo(ClassName, "Session Ending");
Dispose();
};
}
@ -326,7 +326,7 @@ namespace Flow.Launcher
API.StopwatchLogInfo(ClassName, "Dispose cost", () =>
{
Log.Info("|App.Dispose|Begin Flow Launcher dispose ----------------------------------------------------");
API.LogInfo(ClassName, "Begin Flow Launcher dispose ----------------------------------------------------");
if (disposing)
{
@ -336,7 +336,7 @@ namespace Flow.Launcher
_mainVM?.Dispose();
}
Log.Info("|App.Dispose|End Flow Launcher dispose ----------------------------------------------------");
API.LogInfo(ClassName, "End Flow Launcher dispose ----------------------------------------------------");
});
}

View file

@ -4,13 +4,14 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.ViewModel;
namespace Flow.Launcher.Converters;
public class QuerySuggestionBoxConverter : IMultiValueConverter
{
private static readonly string ClassName = nameof(QuerySuggestionBoxConverter);
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// values[0] is TextBox: The textbox displaying the autocomplete suggestion
@ -64,7 +65,7 @@ public class QuerySuggestionBoxConverter : IMultiValueConverter
}
catch (Exception e)
{
Log.Exception(nameof(QuerySuggestionBoxConverter), "fail to convert text for suggestion box", e);
App.API.LogException(ClassName, "fail to convert text for suggestion box", e);
return string.Empty;
}
}

View file

@ -11,6 +11,8 @@ namespace Flow.Launcher.Helper;
public class AutoStartup
{
private static readonly string ClassName = nameof(AutoStartup);
private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
@ -34,7 +36,7 @@ public class AutoStartup
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Ignoring non-critical registry error (querying if enabled): {e}");
App.API.LogError(ClassName, $"Ignoring non-critical registry error (querying if enabled): {e}");
}
return false;
@ -61,7 +63,7 @@ public class AutoStartup
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to check logon task: {e}");
App.API.LogError(ClassName, $"Failed to check logon task: {e}");
}
}
@ -112,7 +114,7 @@ public class AutoStartup
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to disable auto-startup: {e}");
App.API.LogError(ClassName, $"Failed to disable auto-startup: {e}");
throw;
}
}
@ -133,7 +135,7 @@ public class AutoStartup
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}");
App.API.LogError(ClassName, $"Failed to enable auto-startup: {e}");
throw;
}
}
@ -161,7 +163,7 @@ public class AutoStartup
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to schedule logon task: {e}");
App.API.LogError(ClassName, $"Failed to schedule logon task: {e}");
return false;
}
}
@ -176,7 +178,7 @@ public class AutoStartup
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}");
App.API.LogError(ClassName, $"Failed to unschedule logon task: {e}");
return false;
}
}

View file

@ -12,6 +12,8 @@ namespace Flow.Launcher.Helper;
internal static class HotKeyMapper
{
private static readonly string ClassName = nameof(HotKeyMapper);
private static Settings _settings;
private static MainViewModel _mainViewModel;
@ -51,7 +53,7 @@ internal static class HotKeyMapper
}
catch (Exception e)
{
Log.Error(
App.API.LogError(ClassName,
string.Format("|HotkeyMapper.SetWithChefKeys|Error registering hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));
@ -76,7 +78,7 @@ internal static class HotKeyMapper
}
catch (Exception e)
{
Log.Error(
App.API.LogError(ClassName,
string.Format("|HotkeyMapper.SetHotkey|Error registering hotkey {2}: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace,
@ -102,7 +104,7 @@ internal static class HotKeyMapper
}
catch (Exception e)
{
Log.Error(
App.API.LogError(ClassName,
string.Format("|HotkeyMapper.RemoveHotkey|Error removing hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));

View file

@ -12,6 +12,8 @@ namespace Flow.Launcher.Helper;
public static class WallpaperPathRetrieval
{
private static readonly string ClassName = nameof(WallpaperPathRetrieval);
private const int MaxCacheSize = 3;
private static readonly Dictionary<(string, DateTime), ImageBrush> WallpaperCache = new();
private static readonly object CacheLock = new();
@ -29,7 +31,7 @@ public static class WallpaperPathRetrieval
var wallpaperPath = Win32Helper.GetWallpaperPath();
if (string.IsNullOrEmpty(wallpaperPath) || !File.Exists(wallpaperPath))
{
App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Wallpaper path is invalid: {wallpaperPath}");
App.API.LogInfo(ClassName, $"Wallpaper path is invalid: {wallpaperPath}");
var wallpaperColor = GetWallpaperColor();
return new SolidColorBrush(wallpaperColor);
}
@ -54,7 +56,7 @@ public static class WallpaperPathRetrieval
if (originalWidth == 0 || originalHeight == 0)
{
App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
App.API.LogInfo(ClassName, $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
return new SolidColorBrush(Colors.Transparent);
}
@ -95,7 +97,7 @@ public static class WallpaperPathRetrieval
}
catch (Exception ex)
{
App.API.LogException(nameof(WallpaperPathRetrieval), "Error retrieving wallpaper", ex);
App.API.LogException(ClassName, "Error retrieving wallpaper", ex);
return new SolidColorBrush(Colors.Transparent);
}
}
@ -113,7 +115,7 @@ public static class WallpaperPathRetrieval
}
catch (Exception ex)
{
App.API.LogException(nameof(WallpaperPathRetrieval), "Error parsing wallpaper color", ex);
App.API.LogException(ClassName, "Error parsing wallpaper color", ex);
}
}

View file

@ -4,13 +4,13 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
namespace Flow.Launcher
{
public partial class MessageBoxEx : Window
{
private static readonly string ClassName = nameof(MessageBoxEx);
private static MessageBoxEx msgBox;
private static MessageBoxResult _result = MessageBoxResult.None;
@ -59,7 +59,7 @@ namespace Flow.Launcher
}
catch (Exception e)
{
Log.Error($"|MessageBoxEx.Show|An error occurred: {e.Message}");
App.API.LogError(ClassName, $"An error occurred: {e.Message}");
msgBox = null;
return MessageBoxResult.None;
}

View file

@ -5,7 +5,6 @@ using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media.Animation;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
namespace Flow.Launcher
{

View file

@ -1,5 +1,4 @@
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Microsoft.Toolkit.Uwp.Notifications;
using System;
using System.IO;
@ -9,6 +8,8 @@ namespace Flow.Launcher
{
internal static class Notification
{
private static readonly string ClassName = nameof(Notification);
internal static bool legacy = !Win32Helper.IsNotificationSupported();
internal static void Uninstall()
@ -51,12 +52,12 @@ namespace Flow.Launcher
{
// Temporary fix for the Windows 11 notification issue
// Possibly from 22621.1413 or 22621.1485, judging by post time of #2024
Log.Exception("Flow.Launcher.Notification|Notification InvalidOperationException Error", e);
App.API.LogException(ClassName, "Notification InvalidOperationException Error", e);
LegacyShow(title, subTitle, iconPath);
}
catch (Exception e)
{
Log.Exception("Flow.Launcher.Notification|Notification Error", e);
App.API.LogException(ClassName, "Notification Error", e);
LegacyShow(title, subTitle, iconPath);
}
}

View file

@ -2,12 +2,13 @@
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Flow.Launcher.Infrastructure.Logger;
namespace Flow.Launcher
{
public partial class ProgressBoxEx : Window
{
private static readonly string ClassName = nameof(ProgressBoxEx);
private readonly Action _cancelProgress;
private ProgressBoxEx(Action cancelProgress)
@ -47,7 +48,7 @@ namespace Flow.Launcher
}
catch (Exception e)
{
Log.Error($"|ProgressBoxEx.Show|An error occurred: {e.Message}");
App.API.LogError(ClassName, $"An error occurred: {e.Message}");
await reportProgressAsync(null).ConfigureAwait(false);
}

View file

@ -435,16 +435,16 @@ namespace Flow.Launcher
PluginManager.UninstallPluginAsync(pluginMetadata, removePluginSettings);
public long StopwatchLogDebug(string className, string message, Action action, [CallerMemberName] string methodName = "") =>
Stopwatch.Debug($"|{className}.{methodName}|{message}", action);
Stopwatch.Debug(className, message, action, methodName);
public Task<long> StopwatchLogDebugAsync(string className, string message, Func<Task> action, [CallerMemberName] string methodName = "") =>
Stopwatch.DebugAsync($"|{className}.{methodName}|{message}", action);
Stopwatch.DebugAsync(className, message, action, methodName);
public long StopwatchLogInfo(string className, string message, Action action, [CallerMemberName] string methodName = "") =>
Stopwatch.Normal($"|{className}.{methodName}|{message}", action);
Stopwatch.Info(className, message, action, methodName);
public Task<long> StopwatchLogInfoAsync(string className, string message, Func<Task> action, [CallerMemberName] string methodName = "") =>
Stopwatch.NormalAsync($"|{className}.{methodName}|{message}", action);
Stopwatch.InfoAsync(className, message, action, methodName);
#endregion

View file

@ -8,13 +8,15 @@ using System.Windows;
using System.Windows.Documents;
using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Infrastructure.UserSettings;
namespace Flow.Launcher
{
internal partial class ReportWindow
{
private static readonly string ClassName = nameof(ReportWindow);
public ReportWindow(Exception exception)
{
InitializeComponent();
@ -38,7 +40,7 @@ namespace Flow.Launcher
private void SetException(Exception exception)
{
string path = Log.CurrentLogDirectory;
var path = DataLocation.VersionLogDirectory;
var directory = new DirectoryInfo(path);
var log = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();

View file

@ -6,7 +6,6 @@ using System.Windows.Interop;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.SettingPages.Views;
using Flow.Launcher.ViewModel;
using ModernWpf.Controls;
@ -16,7 +15,6 @@ namespace Flow.Launcher;
public partial class SettingWindow
{
private readonly IPublicAPI _api;
private readonly Settings _settings;
private readonly SettingWindowViewModel _viewModel;
@ -26,7 +24,6 @@ public partial class SettingWindow
_settings = Ioc.Default.GetRequiredService<Settings>();
DataContext = viewModel;
_viewModel = viewModel;
_api = Ioc.Default.GetRequiredService<IPublicAPI>();
InitializePosition();
InitializeComponent();
}
@ -49,7 +46,7 @@ public partial class SettingWindow
_settings.SettingWindowTop = Top;
_settings.SettingWindowLeft = Left;
_viewModel.Save();
_api.SavePluginSettings();
App.API.SavePluginSettings();
}
private void OnCloseExecuted(object sender, ExecutedRoutedEventArgs e)

View file

@ -16,7 +16,6 @@ using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
@ -30,6 +29,8 @@ namespace Flow.Launcher.ViewModel
{
#region Private Fields
private static readonly string ClassName = nameof(MainViewModel);
private bool _isQueryRunning;
private Query _lastQuery;
private string _queryTextBeforeLeaveResults;
@ -213,7 +214,7 @@ namespace Flow.Launcher.ViewModel
}
if (!_disposed)
Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends");
App.API.LogError(ClassName, "Unexpected ResultViewUpdate ends");
}
void continueAction(Task t)
@ -221,7 +222,7 @@ namespace Flow.Launcher.ViewModel
#if DEBUG
throw t.Exception;
#else
Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}");
App.API.LogError(ClassName, $"Error happen in task dealing with viewupdate for results. {t.Exception}");
_resultsViewUpdateTask =
Task.Run(UpdateActionAsync).ContinueWith(continueAction, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
#endif
@ -257,7 +258,7 @@ namespace Flow.Launcher.ViewModel
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query,
token)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
App.API.LogError(ClassName, "Unable to add item to Result Update Queue");
}
};
}
@ -891,7 +892,7 @@ namespace Flow.Launcher.ViewModel
#if DEBUG
throw new NotImplementedException("ResultAreaColumn should match ResultAreaColumnPreviewShown/ResultAreaColumnPreviewHidden value");
#else
Log.Error("MainViewModel", "ResultAreaColumnPreviewHidden/ResultAreaColumnPreviewShown int value not implemented", "InternalPreviewVisible");
App.API.LogError(ClassName, "ResultAreaColumnPreviewHidden/ResultAreaColumnPreviewShown int value not implemented", "InternalPreviewVisible");
return false;
#endif
}
@ -1303,7 +1304,7 @@ namespace Flow.Launcher.ViewModel
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query,
token, reSelect)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
App.API.LogError(ClassName, "Unable to add item to Result Update Queue");
}
}
}
@ -1347,8 +1348,8 @@ namespace Flow.Launcher.ViewModel
}
catch (Exception e)
{
Log.Exception(
$"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}",
App.API.LogException(ClassName,
$"Error when expanding shortcut {shortcut.Key}",
e);
}
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing.Text;
using System.IO;
@ -7,7 +6,6 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
@ -15,6 +13,8 @@ namespace Flow.Launcher.ViewModel
{
public class ResultViewModel : BaseModel
{
private static readonly string ClassName = nameof(ResultViewModel);
private static readonly PrivateFontCollection FontCollection = new();
private static readonly Dictionary<string, string> Fonts = new();
@ -232,8 +232,8 @@ namespace Flow.Launcher.ViewModel
}
catch (Exception e)
{
Log.Exception(
$"|ResultViewModel.LoadImageInternalAsync|IcoPath is empty and exception when calling IconDelegate for result <{Result.Title}> of plugin <{Result.PluginDirectory}>",
App.API.LogException(ClassName,
$"IcoPath is empty and exception when calling IconDelegate for result <{Result.Title}> of plugin <{Result.PluginDirectory}>",
e);
}
}

View file

@ -15,6 +15,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark;
public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContextMenu, IDisposable
{
private static readonly string ClassName = nameof(Main);
internal static string _faviconCacheDir;
internal static PluginInitContext _context;
@ -221,7 +223,7 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex
catch (Exception e)
{
var message = "Failed to set url in clipboard";
_context.API.LogException(nameof(Main), message, e);
_context.API.LogException(ClassName, message, e);
_context.API.ShowMsg(message);

View file

@ -15,6 +15,8 @@ namespace Flow.Launcher.Plugin.Explorer
{
internal class ContextMenu : IContextMenu
{
private static readonly string ClassName = nameof(ContextMenu);
private PluginInitContext Context { get; set; }
private Settings Settings { get; set; }
@ -469,7 +471,7 @@ namespace Flow.Launcher.Plugin.Explorer
private void LogException(string message, Exception e)
{
Context.API.LogException(nameof(ContextMenu), message, e);
Context.API.LogException(ClassName, message, e);
}
private static bool CanRunAsDifferentUser(string path)

View file

@ -9,6 +9,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
{
public static class DirectoryInfoSearch
{
private static readonly string ClassName = nameof(DirectoryInfoSearch);
internal static IEnumerable<SearchResult> TopLevelDirectorySearch(Query query, string search, CancellationToken token)
{
var criteria = ConstructSearchCriteria(search);
@ -75,7 +77,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
}
catch (Exception e)
{
Main.Context.API.LogException(nameof(DirectoryInfoSearch), "Error occurred while searching path", e);
Main.Context.API.LogException(ClassName, "Error occurred while searching path", e);
throw;
}

View file

@ -12,6 +12,8 @@ namespace Flow.Launcher.Plugin.ProcessKiller
{
internal class ProcessHelper
{
private static readonly string ClassName = nameof(ProcessHelper);
private readonly HashSet<string> _systemProcessList = new()
{
"conhost",
@ -131,7 +133,7 @@ namespace Flow.Launcher.Plugin.ProcessKiller
}
catch (Exception e)
{
context.API.LogException($"{nameof(ProcessHelper)}", $"Failed to kill process {p.ProcessName}", e);
context.API.LogException(ClassName, $"Failed to kill process {p.ProcessName}", e);
}
}

View file

@ -109,7 +109,6 @@ namespace Flow.Launcher.Plugin.Program
}
catch (OperationCanceledException)
{
Context.API.LogDebug(ClassName, "Query operation cancelled");
return emptyResults;
}
finally

View file

@ -11,6 +11,8 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
public class Baidu : SuggestionSource
{
private static readonly string ClassName = nameof(Baidu);
private readonly Regex _reg = new Regex("window.baidu.sug\\((.*)\\)");
public override async Task<List<string>> SuggestionsAsync(string query, CancellationToken token)
@ -24,7 +26,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
}
catch (Exception e) when (e is HttpRequestException or {InnerException: TimeoutException})
{
Main._context.API.LogException(nameof(Baidu), "Can't get suggestion from Baidu", e);
Main._context.API.LogException(ClassName, "Can't get suggestion from Baidu", e);
return null;
}
@ -39,7 +41,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
}
catch (JsonException e)
{
Main._context.API.LogException(nameof(Baidu), "Can't parse suggestions", e);
Main._context.API.LogException(ClassName, "Can't parse suggestions", e);
return new List<string>();
}

View file

@ -10,6 +10,8 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
public class Bing : SuggestionSource
{
private static readonly string ClassName = nameof(Bing);
public override async Task<List<string>> SuggestionsAsync(string query, CancellationToken token)
{
try
@ -33,12 +35,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
}
catch (Exception e) when (e is HttpRequestException or { InnerException: TimeoutException })
{
Main._context.API.LogException(nameof(Bing), "Can't get suggestion from Bing", e);
Main._context.API.LogException(ClassName, "Can't get suggestion from Bing", e);
return null;
}
catch (JsonException e)
{
Main._context.API.LogException(nameof(Bing), "Can't parse suggestions", e);
Main._context.API.LogException(ClassName, "Can't parse suggestions", e);
return new List<string>();
}
}

View file

@ -10,6 +10,8 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
public class DuckDuckGo : SuggestionSource
{
private static readonly string ClassName = nameof(DuckDuckGo);
public override async Task<List<string>> SuggestionsAsync(string query, CancellationToken token)
{
// When the search query is empty, DuckDuckGo returns `[]`. When it's not empty, it returns data
@ -34,12 +36,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
}
catch (Exception e) when (e is HttpRequestException or {InnerException: TimeoutException})
{
Main._context.API.LogException(nameof(DuckDuckGo), "Can't get suggestion from DuckDuckGo", e);
Main._context.API.LogException(ClassName, "Can't get suggestion from DuckDuckGo", e);
return null;
}
catch (JsonException e)
{
Main._context.API.LogException(nameof(DuckDuckGo), "Can't parse suggestions", e);
Main._context.API.LogException(ClassName, "Can't parse suggestions", e);
return new List<string>();
}
}

View file

@ -10,6 +10,8 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
public class Google : SuggestionSource
{
private static readonly string ClassName = nameof(Google);
public override async Task<List<string>> SuggestionsAsync(string query, CancellationToken token)
{
try
@ -27,12 +29,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
}
catch (Exception e) when (e is HttpRequestException or {InnerException: TimeoutException})
{
Main._context.API.LogException(nameof(Google), "Can't get suggestion from Google", e);
Main._context.API.LogException(ClassName, "Can't get suggestion from Google", e);
return null;
}
catch (JsonException e)
{
Main._context.API.LogException(nameof(Google), "Can't parse suggestions", e);
Main._context.API.LogException(ClassName, "Can't parse suggestions", e);
return new List<string>();
}
}

View file

@ -11,10 +11,12 @@ namespace Flow.Launcher.Plugin.WindowsSettings
{
_api = api;
}
public static void Exception(string message, Exception exception, Type type, [CallerMemberName] string methodName = "")
{
_api?.LogException(type.FullName, message, exception, methodName);
}
public static void Warn(string message, Type type, [CallerMemberName] string methodName = "")
{
_api?.LogWarn(type.FullName, message, methodName);