mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #80 from JohnTheGr8/refactor_core_infra
Refactoring in Flow.Launcher.Core
This commit is contained in:
commit
4c20dbc48d
8 changed files with 154 additions and 240 deletions
|
|
@ -32,10 +32,9 @@ namespace Flow.Launcher.Core.Configuration
|
|||
try
|
||||
{
|
||||
MoveUserDataFolder(DataLocation.PortableDataPath, DataLocation.RoamingDataPath);
|
||||
#if DEBUG
|
||||
#if !DEBUG
|
||||
// Create shortcuts and uninstaller are not required in debug mode,
|
||||
// otherwise will repoint the path of the actual installed production version to the debug version
|
||||
#else
|
||||
CreateShortcuts();
|
||||
CreateUninstallerEntry();
|
||||
#endif
|
||||
|
|
@ -48,10 +47,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if !DEBUG
|
||||
Log.Exception("Portable", "Error occured while disabling portable mode", e);
|
||||
#endif
|
||||
throw;
|
||||
Log.Exception("|Portable.DisablePortableMode|Error occured while disabling portable mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,10 +56,9 @@ namespace Flow.Launcher.Core.Configuration
|
|||
try
|
||||
{
|
||||
MoveUserDataFolder(DataLocation.RoamingDataPath, DataLocation.PortableDataPath);
|
||||
#if DEBUG
|
||||
#if !DEBUG
|
||||
// Remove shortcuts and uninstaller are not required in debug mode,
|
||||
// otherwise will delete the actual installed production version
|
||||
#else
|
||||
RemoveShortcuts();
|
||||
RemoveUninstallerEntry();
|
||||
#endif
|
||||
|
|
@ -76,10 +71,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if !DEBUG
|
||||
Log.Exception("Portable", "Error occured while enabling portable mode", e);
|
||||
#endif
|
||||
throw;
|
||||
Log.Exception("|Portable.EnablePortableMode|Error occured while enabling portable mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,14 +117,13 @@ namespace Flow.Launcher.Core.Configuration
|
|||
public void CreateUninstallerEntry()
|
||||
{
|
||||
var uninstallRegSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
// NB: Sometimes the Uninstall key doesn't exist
|
||||
using (var parentKey =
|
||||
RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
|
||||
.CreateSubKey("Uninstall", RegistryKeyPermissionCheck.ReadWriteSubTree)) {; }
|
||||
|
||||
var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
|
||||
.CreateSubKey(uninstallRegSubKey + "\\" + Constant.FlowLauncher, RegistryKeyPermissionCheck.ReadWriteSubTree);
|
||||
key.SetValue("DisplayIcon", Constant.ApplicationDirectory + "\\app.ico", RegistryValueKind.String);
|
||||
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default))
|
||||
using (var subKey1 = baseKey.CreateSubKey(uninstallRegSubKey, RegistryKeyPermissionCheck.ReadWriteSubTree))
|
||||
using (var subKey2 = subKey1.CreateSubKey(Constant.FlowLauncher, RegistryKeyPermissionCheck.ReadWriteSubTree))
|
||||
{
|
||||
subKey2.SetValue("DisplayIcon", Path.Combine(Constant.ApplicationDirectory, "app.ico"), RegistryValueKind.String);
|
||||
}
|
||||
|
||||
using (var portabilityUpdater = NewUpdateManager())
|
||||
{
|
||||
|
|
@ -142,7 +133,10 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
internal void IndicateDeletion(string filePathTodelete)
|
||||
{
|
||||
using (StreamWriter sw = File.CreateText(filePathTodelete + "\\" + DataLocation.DeletionIndicatorFile)){}
|
||||
var deleteFilePath = Path.Combine(filePathTodelete, DataLocation.DeletionIndicatorFile);
|
||||
using (var _ = File.CreateText(deleteFilePath))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>
|
||||
|
|
@ -152,21 +146,18 @@ namespace Flow.Launcher.Core.Configuration
|
|||
public void PreStartCleanUpAfterPortabilityUpdate()
|
||||
{
|
||||
// Specify here so this method does not rely on other environment variables to initialise
|
||||
var portableDataPath = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location.NonNull()).ToString(), "UserData");
|
||||
var roamingDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher");
|
||||
var portableDataDir = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location.NonNull()).ToString(), "UserData");
|
||||
var roamingDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher");
|
||||
|
||||
bool DataLocationPortableDeleteRequired = false;
|
||||
bool DataLocationRoamingDeleteRequired = false;
|
||||
// Get full path to the .dead files for each case
|
||||
var portableDataDeleteFilePath = Path.Combine(portableDataDir, DataLocation.DeletionIndicatorFile);
|
||||
var roamingDataDeleteFilePath = Path.Combine(roamingDataDir, DataLocation.DeletionIndicatorFile);
|
||||
|
||||
if ((roamingDataPath + "\\" + DataLocation.DeletionIndicatorFile).FileExits())
|
||||
DataLocationRoamingDeleteRequired = true;
|
||||
|
||||
if ((portableDataPath + "\\" + DataLocation.DeletionIndicatorFile).FileExits())
|
||||
DataLocationPortableDeleteRequired = true;
|
||||
|
||||
if (DataLocationRoamingDeleteRequired)
|
||||
// If the data folder in %appdata% is marked for deletion,
|
||||
// delete it and prompt the user to pick the portable data location
|
||||
if (File.Exists(roamingDataDeleteFilePath))
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(roamingDataPath);
|
||||
FilesFolders.RemoveFolderIfExists(roamingDataDir);
|
||||
|
||||
if (MessageBox.Show("Flow Launcher has detected you enabled portable mode, " +
|
||||
"would you like to move it to a different location?", string.Empty,
|
||||
|
|
@ -176,18 +167,15 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(DataLocationPortableDeleteRequired)
|
||||
// Otherwise, if the portable data folder is marked for deletion,
|
||||
// delete it and notify the user about it.
|
||||
else if (File.Exists(portableDataDeleteFilePath))
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(portableDataPath);
|
||||
FilesFolders.RemoveFolderIfExists(portableDataDir);
|
||||
|
||||
MessageBox.Show("Flow Launcher has detected you disabled portable mode, " +
|
||||
"the relevant shortcuts and uninstaller entry have been created");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +184,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
var roamingLocationExists = DataLocation.RoamingDataPath.LocationExists();
|
||||
var portableLocationExists = DataLocation.PortableDataPath.LocationExists();
|
||||
|
||||
if(roamingLocationExists && portableLocationExists)
|
||||
if (roamingLocationExists && portableLocationExists)
|
||||
{
|
||||
MessageBox.Show(string.Format("Flow Launcher detected your user data exists both in {0} and " +
|
||||
"{1}. {2}{2}Please delete {1} in order to proceed. No changes have occured.",
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Infrastructure.Exception;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
|
|
@ -13,24 +12,16 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
internal abstract class PluginConfig
|
||||
{
|
||||
private const string PluginConfigName = "plugin.json";
|
||||
private static readonly List<PluginMetadata> PluginMetadatas = new List<PluginMetadata>();
|
||||
|
||||
/// <summary>
|
||||
/// Parse plugin metadata in giving directories
|
||||
/// Parse plugin metadata in the given directories
|
||||
/// </summary>
|
||||
/// <param name="pluginDirectories"></param>
|
||||
/// <returns></returns>
|
||||
public static List<PluginMetadata> Parse(string[] pluginDirectories)
|
||||
{
|
||||
PluginMetadatas.Clear();
|
||||
var allPluginMetadata = new List<PluginMetadata>();
|
||||
var directories = pluginDirectories.SelectMany(Directory.GetDirectories);
|
||||
ParsePluginConfigs(directories);
|
||||
return PluginMetadatas;
|
||||
}
|
||||
|
||||
private static void ParsePluginConfigs(IEnumerable<string> directories)
|
||||
{
|
||||
// todo use linq when diable plugin is implmented since parallel.foreach + list is not thread saft
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
|
|
@ -50,15 +41,17 @@ namespace Flow.Launcher.Core.Plugin
|
|||
PluginMetadata metadata = GetPluginMetadata(directory);
|
||||
if (metadata != null)
|
||||
{
|
||||
PluginMetadatas.Add(metadata);
|
||||
allPluginMetadata.Add(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allPluginMetadata;
|
||||
}
|
||||
|
||||
private static PluginMetadata GetPluginMetadata(string pluginDirectory)
|
||||
{
|
||||
string configPath = Path.Combine(pluginDirectory, PluginConfigName);
|
||||
string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
|
||||
if (!File.Exists(configPath))
|
||||
{
|
||||
Log.Error($"|PluginConfig.GetPluginMetadata|Didn't find config file <{configPath}>");
|
||||
|
|
@ -81,7 +74,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (!AllowedLanguage.IsAllowed(metadata.Language))
|
||||
{
|
||||
Log.Error($"|PluginConfig.GetPluginMetadata|Invalid language <{metadata.Language}> for config <{configPath}>");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -13,28 +15,28 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string tempFoler = Path.Combine(Path.GetTempPath(), "flowlauncher\\plugins");
|
||||
if (Directory.Exists(tempFoler))
|
||||
string tempFolder = Path.Combine(Path.GetTempPath(), "flowlauncher", "plugins");
|
||||
if (Directory.Exists(tempFolder))
|
||||
{
|
||||
Directory.Delete(tempFoler, true);
|
||||
Directory.Delete(tempFolder, true);
|
||||
}
|
||||
UnZip(path, tempFoler, true);
|
||||
UnZip(path, tempFolder, true);
|
||||
|
||||
string iniPath = Path.Combine(tempFoler, "plugin.json");
|
||||
if (!File.Exists(iniPath))
|
||||
string jsonPath = Path.Combine(tempFolder, Constant.PluginMetadataFileName);
|
||||
if (!File.Exists(jsonPath))
|
||||
{
|
||||
MessageBox.Show("Install failed: plugin config is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
PluginMetadata plugin = GetMetadataFromJson(tempFoler);
|
||||
PluginMetadata plugin = GetMetadataFromJson(tempFolder);
|
||||
if (plugin == null || plugin.Name == null)
|
||||
{
|
||||
MessageBox.Show("Install failed: plugin config is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
string pluginFolerPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory;
|
||||
string pluginFolderPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory;
|
||||
|
||||
string newPluginName = plugin.Name
|
||||
.Replace("/", "_")
|
||||
|
|
@ -46,7 +48,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
.Replace("*", "_")
|
||||
.Replace("|", "_")
|
||||
+ "-" + Guid.NewGuid();
|
||||
string newPluginPath = Path.Combine(pluginFolerPath, newPluginName);
|
||||
|
||||
string newPluginPath = Path.Combine(pluginFolderPath, newPluginName);
|
||||
|
||||
string content = $"Do you want to install following plugin?{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"Name: {plugin.Name}{Environment.NewLine}" +
|
||||
$"Version: {plugin.Version}{Environment.NewLine}" +
|
||||
|
|
@ -71,8 +75,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close();
|
||||
}
|
||||
|
||||
UnZip(path, newPluginPath, true);
|
||||
Directory.Delete(tempFoler, true);
|
||||
Directory.Move(tempFolder, newPluginPath);
|
||||
|
||||
//exsiting plugins may be has loaded by application,
|
||||
//if we try to delelte those kind of plugins, we will get a error that indicate the
|
||||
|
|
@ -94,7 +97,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
private static PluginMetadata GetMetadataFromJson(string pluginDirectory)
|
||||
{
|
||||
string configPath = Path.Combine(pluginDirectory, "plugin.json");
|
||||
string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
|
||||
PluginMetadata metadata;
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
|
|
@ -107,36 +110,20 @@ namespace Flow.Launcher.Core.Plugin
|
|||
metadata = JsonConvert.DeserializeObject<PluginMetadata>(File.ReadAllText(configPath));
|
||||
metadata.PluginDirectory = pluginDirectory;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
string error = $"Parse plugin config {configPath} failed: json format is not valid";
|
||||
#if (DEBUG)
|
||||
{
|
||||
throw new Exception(error);
|
||||
}
|
||||
#endif
|
||||
Log.Exception($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid json format", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (!AllowedLanguage.IsAllowed(metadata.Language))
|
||||
{
|
||||
string error = $"Parse plugin config {configPath} failed: invalid language {metadata.Language}";
|
||||
#if (DEBUG)
|
||||
{
|
||||
throw new Exception(error);
|
||||
}
|
||||
#endif
|
||||
Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid language {metadata.Language}");
|
||||
return null;
|
||||
}
|
||||
if (!File.Exists(metadata.ExecuteFilePath))
|
||||
{
|
||||
string error = $"Parse plugin config {configPath} failed: ExecuteFile {metadata.ExecuteFilePath} didn't exist";
|
||||
#if (DEBUG)
|
||||
{
|
||||
throw new Exception(error);
|
||||
}
|
||||
#endif
|
||||
Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: file {metadata.ExecuteFilePath} doesn't exist");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -144,58 +131,38 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// unzip
|
||||
/// unzip plugin contents to the given directory.
|
||||
/// </summary>
|
||||
/// <param name="zipedFile">The ziped file.</param>
|
||||
/// <param name="strDirectory">The STR directory.</param>
|
||||
/// <param name="zipFile">The path to the zip file.</param>
|
||||
/// <param name="strDirectory">The output directory.</param>
|
||||
/// <param name="overWrite">overwirte</param>
|
||||
private static void UnZip(string zipedFile, string strDirectory, bool overWrite)
|
||||
private static void UnZip(string zipFile, string strDirectory, bool overWrite)
|
||||
{
|
||||
if (strDirectory == "")
|
||||
strDirectory = Directory.GetCurrentDirectory();
|
||||
if (!strDirectory.EndsWith("\\"))
|
||||
strDirectory = strDirectory + "\\";
|
||||
|
||||
using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipedFile)))
|
||||
using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(zipFile)))
|
||||
{
|
||||
ZipEntry theEntry;
|
||||
|
||||
while ((theEntry = s.GetNextEntry()) != null)
|
||||
while ((theEntry = zipStream.GetNextEntry()) != null)
|
||||
{
|
||||
string directoryName = "";
|
||||
string pathToZip = "";
|
||||
pathToZip = theEntry.Name;
|
||||
var pathToZip = theEntry.Name;
|
||||
var directoryName = String.IsNullOrEmpty(pathToZip) ? "" : Path.GetDirectoryName(pathToZip);
|
||||
var fileName = Path.GetFileName(pathToZip);
|
||||
var destinationDir = Path.Combine(strDirectory, directoryName);
|
||||
var destinationFile = Path.Combine(destinationDir, fileName);
|
||||
|
||||
if (pathToZip != "")
|
||||
directoryName = Path.GetDirectoryName(pathToZip) + "\\";
|
||||
Directory.CreateDirectory(destinationDir);
|
||||
|
||||
string fileName = Path.GetFileName(pathToZip);
|
||||
if (String.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overWrite))
|
||||
continue;
|
||||
|
||||
Directory.CreateDirectory(strDirectory + directoryName);
|
||||
|
||||
if (fileName != "")
|
||||
using (FileStream streamWriter = File.Create(destinationFile))
|
||||
{
|
||||
if ((File.Exists(strDirectory + directoryName + fileName) && overWrite) || (!File.Exists(strDirectory + directoryName + fileName)))
|
||||
{
|
||||
using (FileStream streamWriter = File.Create(strDirectory + directoryName + fileName))
|
||||
{
|
||||
byte[] data = new byte[2048];
|
||||
while (true)
|
||||
{
|
||||
int size = s.Read(data, 0, data.Length);
|
||||
|
||||
if (size > 0)
|
||||
streamWriter.Write(data, 0, size);
|
||||
else
|
||||
break;
|
||||
}
|
||||
streamWriter.Close();
|
||||
}
|
||||
}
|
||||
zipStream.CopyTo(streamWriter);
|
||||
}
|
||||
}
|
||||
|
||||
s.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
private static IEnumerable<PluginPair> _contextMenuPlugins;
|
||||
|
||||
/// <summary>
|
||||
/// Directories that will hold Flow Launcher plugin directory
|
||||
/// </summary>
|
||||
|
||||
public static List<PluginPair> AllPlugins { get; private set; }
|
||||
public static readonly List<PluginPair> GlobalPlugins = new List<PluginPair>();
|
||||
public static readonly Dictionary<string, PluginPair> NonGlobalPlugins = new Dictionary<string, PluginPair>();
|
||||
|
|
@ -32,27 +28,18 @@ namespace Flow.Launcher.Core.Plugin
|
|||
// todo happlebao, this should not be public, the indicator function should be embeded
|
||||
public static PluginsSettings Settings;
|
||||
private static List<PluginMetadata> _metadatas;
|
||||
private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory };
|
||||
|
||||
private static void ValidateUserDirectory()
|
||||
{
|
||||
if (!Directory.Exists(DataLocation.PluginsDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(DataLocation.PluginsDirectory);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Directories that will hold Flow Launcher plugin directory
|
||||
/// </summary>
|
||||
private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory };
|
||||
|
||||
private static void DeletePythonBinding()
|
||||
{
|
||||
const string binding = "flowlauncher.py";
|
||||
var directory = DataLocation.PluginsDirectory;
|
||||
foreach (var subDirectory in Directory.GetDirectories(directory))
|
||||
foreach (var subDirectory in Directory.GetDirectories(DataLocation.PluginsDirectory))
|
||||
{
|
||||
var path = Path.Combine(subDirectory, binding);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
File.Delete(Path.Combine(subDirectory, binding));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +63,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
static PluginManager()
|
||||
{
|
||||
ValidateUserDirectory();
|
||||
// validate user directory
|
||||
Directory.CreateDirectory(DataLocation.PluginsDirectory);
|
||||
// force old plugins use new python binding
|
||||
DeletePythonBinding();
|
||||
}
|
||||
|
|
@ -132,9 +120,10 @@ namespace Flow.Launcher.Core.Plugin
|
|||
GlobalPlugins.Add(plugin);
|
||||
|
||||
// Plugins may have multiple ActionKeywords, eg. WebSearch
|
||||
plugin.Metadata.ActionKeywords.Where(x => x != Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.ForEach(x => NonGlobalPlugins[x] = plugin);
|
||||
plugin.Metadata.ActionKeywords
|
||||
.Where(x => x != Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.ForEach(x => NonGlobalPlugins[x] = plugin);
|
||||
}
|
||||
|
||||
if (failedPlugins.Any())
|
||||
|
|
@ -164,9 +153,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public static List<Result> QueryForPlugin(PluginPair pair, Query query)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
try
|
||||
{
|
||||
List<Result> results = null;
|
||||
var metadata = pair.Metadata;
|
||||
var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () =>
|
||||
{
|
||||
|
|
@ -175,13 +164,12 @@ namespace Flow.Launcher.Core.Plugin
|
|||
});
|
||||
metadata.QueryCount += 1;
|
||||
metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
|
||||
return results;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
|
||||
return new List<Result>();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static void UpdatePluginMetadata(List<Result> results, PluginMetadata metadata, Query query)
|
||||
|
|
@ -221,47 +209,34 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public static List<Result> GetContextMenusForPlugin(Result result)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID);
|
||||
if (pluginPair != null)
|
||||
{
|
||||
var metadata = pluginPair.Metadata;
|
||||
var plugin = (IContextMenu)pluginPair.Plugin;
|
||||
|
||||
try
|
||||
{
|
||||
var results = plugin.LoadContextMenus(result);
|
||||
results = plugin.LoadContextMenus(result);
|
||||
foreach (var r in results)
|
||||
{
|
||||
r.PluginDirectory = metadata.PluginDirectory;
|
||||
r.PluginID = metadata.ID;
|
||||
r.PluginDirectory = pluginPair.Metadata.PluginDirectory;
|
||||
r.PluginID = pluginPair.Metadata.ID;
|
||||
r.OriginQuery = result.OriginQuery;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{metadata.Name}>", e);
|
||||
return new List<Result>();
|
||||
Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new List<Result>();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static bool ActionKeywordRegistered(string actionKeyword)
|
||||
{
|
||||
if (actionKeyword != Query.GlobalPluginWildcardSign &&
|
||||
NonGlobalPlugins.ContainsKey(actionKeyword))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return actionKeyword != Query.GlobalPluginWildcardSign
|
||||
&& NonGlobalPlugins.ContainsKey(actionKeyword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -299,7 +274,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
GlobalPlugins.Remove(plugin);
|
||||
}
|
||||
|
||||
if(oldActionkeyword != Query.GlobalPluginWildcardSign)
|
||||
if (oldActionkeyword != Query.GlobalPluginWildcardSign)
|
||||
NonGlobalPlugins.Remove(oldActionkeyword);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
|
||||
{
|
||||
var dotnetPlugins = DotNetPlugins(metadatas).ToList();
|
||||
var dotnetPlugins = DotNetPlugins(metadatas);
|
||||
var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory);
|
||||
var executablePlugins = ExecutablePlugins(metadatas);
|
||||
var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
|
||||
|
|
@ -46,75 +46,58 @@ namespace Flow.Launcher.Core.Plugin
|
|||
var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin)));
|
||||
var plugin = (IPlugin)Activator.CreateInstance(type);
|
||||
#else
|
||||
Assembly assembly;
|
||||
Assembly assembly = null;
|
||||
IPlugin plugin = null;
|
||||
|
||||
try
|
||||
{
|
||||
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
Type type;
|
||||
try
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
|
||||
type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin)));
|
||||
var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin)));
|
||||
|
||||
plugin = (IPlugin)Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception e) when (assembly == null)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
|
||||
return;
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
|
||||
return;
|
||||
}
|
||||
|
||||
IPlugin plugin;
|
||||
try
|
||||
{
|
||||
plugin = (IPlugin)Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
|
||||
}
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
PluginPair pair = new PluginPair
|
||||
plugins.Add(new PluginPair
|
||||
{
|
||||
Plugin = plugin,
|
||||
Metadata = metadata
|
||||
};
|
||||
plugins.Add(pair);
|
||||
});
|
||||
});
|
||||
metadata.InitTime += milliseconds;
|
||||
|
||||
}
|
||||
|
||||
if (erroredPlugins.Count > 0)
|
||||
{
|
||||
var errorPluginString = "";
|
||||
var errorPluginString = String.Join(Environment.NewLine, erroredPlugins);
|
||||
|
||||
var errorMessage = "The following "
|
||||
+ (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ")
|
||||
+ "errored and cannot be loaded:";
|
||||
|
||||
erroredPlugins.ForEach(x => errorPluginString += x + Environment.NewLine);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
|
||||
|
|
@ -127,65 +110,74 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return plugins;
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, string pythonDirecotry)
|
||||
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, string pythonDirectory)
|
||||
{
|
||||
var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.Python);
|
||||
string filename;
|
||||
|
||||
if (string.IsNullOrEmpty(pythonDirecotry))
|
||||
// try to set Constant.PythonPath, either from
|
||||
// PATH or from the given pythonDirectory
|
||||
if (string.IsNullOrEmpty(pythonDirectory))
|
||||
{
|
||||
var paths = Environment.GetEnvironmentVariable(PATH);
|
||||
if (paths != null)
|
||||
{
|
||||
var pythonPaths = paths.Split(';').Where(p => p.ToLower().Contains(Python));
|
||||
if (pythonPaths.Any())
|
||||
var pythonInPath = paths
|
||||
.Split(';')
|
||||
.Where(p => p.ToLower().Contains(Python))
|
||||
.Any();
|
||||
|
||||
if (pythonInPath)
|
||||
{
|
||||
filename = PythonExecutable;
|
||||
Constant.PythonPath = PythonExecutable;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|PluginsLoader.PythonPlugins|Python can't be found in PATH.");
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|PluginsLoader.PythonPlugins|PATH environment variable is not set.");
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = Path.Combine(pythonDirecotry, PythonExecutable);
|
||||
var path = Path.Combine(pythonDirectory, PythonExecutable);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
filename = path;
|
||||
Constant.PythonPath = path;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|PluginsLoader.PythonPlugins|Can't find python executable in <b ");
|
||||
return new List<PluginPair>();
|
||||
Log.Error($"|PluginsLoader.PythonPlugins|Can't find python executable in {path}");
|
||||
}
|
||||
}
|
||||
Constant.PythonPath = filename;
|
||||
var plugins = metadatas.Select(metadata => new PluginPair
|
||||
|
||||
// if we have a path to the python executable,
|
||||
// load every python plugin pair.
|
||||
if (String.IsNullOrEmpty(Constant.PythonPath))
|
||||
{
|
||||
Plugin = new PythonPlugin(filename),
|
||||
Metadata = metadata
|
||||
});
|
||||
return plugins;
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return source
|
||||
.Where(o => o.Language.ToUpper() == AllowedLanguage.Python)
|
||||
.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new PythonPlugin(Constant.PythonPath),
|
||||
Metadata = metadata
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
||||
{
|
||||
var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.Executable);
|
||||
|
||||
var plugins = metadatas.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new ExecutablePlugin(metadata.ExecuteFilePath),
|
||||
Metadata = metadata
|
||||
});
|
||||
return plugins;
|
||||
return source
|
||||
.Where(o => o.Language.ToUpper() == AllowedLanguage.Executable)
|
||||
.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new ExecutablePlugin(metadata.ExecuteFilePath),
|
||||
Metadata = metadata
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ namespace Flow.Launcher.Core
|
|||
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
|
||||
FilesFolders.Copy(DataLocation.PortableDataPath, targetDestination);
|
||||
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination))
|
||||
MessageBox.Show(string.Format("Flow Launcher was not able to move your user profile data to the new update version. Please manually" +
|
||||
"move your profile data folder from {0} to {1}", DataLocation.PortableDataPath, targetDestination));
|
||||
MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " +
|
||||
$"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
{
|
||||
public const string FlowLauncher = "Flow.Launcher";
|
||||
public const string Plugins = "Plugins";
|
||||
public const string PluginMetadataFileName = "plugin.json";
|
||||
|
||||
public const string ApplicationFileName = FlowLauncher + ".exe";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -17,7 +17,6 @@ namespace Flow.Launcher.Plugin.PluginManagement
|
|||
public class Main : IPlugin, IPluginI18n
|
||||
{
|
||||
private static string APIBASE = "http://api.wox.one";
|
||||
private static string PluginConfigName = "plugin.json";
|
||||
private static string pluginSearchUrl = APIBASE + "/plugin/search/";
|
||||
private const string ListCommand = "list";
|
||||
private const string InstallCommand = "install";
|
||||
|
|
|
|||
Loading…
Reference in a new issue