diff --git a/.gitignore b/.gitignore
index 28366cd03df..a293100e615 100644
--- a/.gitignore
+++ b/.gitignore
@@ -300,4 +300,8 @@ migrateToAutomaticPackageRestore.ps1
*.pyc
*.diagsession
Output-Performance.txt
-*.diff
\ No newline at end of file
+*.diff
+
+# vscode
+.vscode
+.history
\ No newline at end of file
diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
index 9f146a457fb..189a6669ec1 100644
--- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj
+++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
@@ -55,7 +55,6 @@
-
diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs
deleted file mode 100644
index 7b980a3eea0..00000000000
--- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs
+++ /dev/null
@@ -1,169 +0,0 @@
-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
-{
- internal class PluginInstaller
- {
- internal static void Install(string path)
- {
- if (File.Exists(path))
- {
- string tempFolder = Path.Combine(Path.GetTempPath(), "flowlauncher", "plugins");
- if (Directory.Exists(tempFolder))
- {
- Directory.Delete(tempFolder, true);
- }
- UnZip(path, tempFolder, true);
-
- string jsonPath = Path.Combine(tempFolder, Constant.PluginMetadataFileName);
- if (!File.Exists(jsonPath))
- {
- MessageBox.Show("Install failed: plugin config is missing");
- return;
- }
-
- PluginMetadata plugin = GetMetadataFromJson(tempFolder);
- if (plugin == null || plugin.Name == null)
- {
- MessageBox.Show("Install failed: plugin config is invalid");
- return;
- }
-
- string pluginFolderPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory;
-
- string newPluginName = plugin.Name
- .Replace("/", "_")
- .Replace("\\", "_")
- .Replace(":", "_")
- .Replace("<", "_")
- .Replace(">", "_")
- .Replace("?", "_")
- .Replace("*", "_")
- .Replace("|", "_")
- + "-" + Guid.NewGuid();
-
- 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}" +
- $"Author: {plugin.Author}";
- PluginPair existingPlugin = PluginManager.GetPluginForId(plugin.ID);
-
- if (existingPlugin != null)
- {
- content = $"Do you want to update following plugin?{Environment.NewLine}{Environment.NewLine}" +
- $"Name: {plugin.Name}{Environment.NewLine}" +
- $"Old Version: {existingPlugin.Metadata.Version}" +
- $"{Environment.NewLine}New Version: {plugin.Version}" +
- $"{Environment.NewLine}Author: {plugin.Author}";
- }
-
- var result = MessageBox.Show(content, "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question);
- if (result == MessageBoxResult.Yes)
- {
- if (existingPlugin != null && Directory.Exists(existingPlugin.Metadata.PluginDirectory))
- {
- //when plugin is in use, we can't delete them. That's why we need to make plugin folder a random name
- File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close();
- }
-
- 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
- //file is been used now.
- //current solution is to restart Flow Launcher. Ugly.
- //if (MainWindow.Initialized)
- //{
- // Plugins.Initialize();
- //}
- if (MessageBox.Show($"You have installed plugin {plugin.Name} successfully.{Environment.NewLine}" +
- "Restart Flow Launcher to take effect?",
- "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
- {
- PluginManager.API.RestartApp();
- }
- }
- }
- }
-
- private static PluginMetadata GetMetadataFromJson(string pluginDirectory)
- {
- string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
- PluginMetadata metadata;
-
- if (!File.Exists(configPath))
- {
- return null;
- }
-
- try
- {
- metadata = JsonConvert.DeserializeObject(File.ReadAllText(configPath));
- metadata.PluginDirectory = pluginDirectory;
- }
- catch (Exception e)
- {
- Log.Exception($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid json format", e);
- return null;
- }
-
- if (!AllowedLanguage.IsAllowed(metadata.Language))
- {
- Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid language {metadata.Language}");
- return null;
- }
- if (!File.Exists(metadata.ExecuteFilePath))
- {
- Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: file {metadata.ExecuteFilePath} doesn't exist");
- return null;
- }
-
- return metadata;
- }
-
- ///
- /// unzip plugin contents to the given directory.
- ///
- /// The path to the zip file.
- /// The output directory.
- /// overwirte
- private static void UnZip(string zipFile, string strDirectory, bool overWrite)
- {
- if (strDirectory == "")
- strDirectory = Directory.GetCurrentDirectory();
-
- using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(zipFile)))
- {
- ZipEntry theEntry;
-
- while ((theEntry = zipStream.GetNextEntry()) != null)
- {
- 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);
-
- Directory.CreateDirectory(destinationDir);
-
- if (String.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overWrite))
- continue;
-
- using (FileStream streamWriter = File.Create(destinationFile))
- {
- zipStream.CopyTo(streamWriter);
- }
- }
- }
- }
- }
-}
diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs
index 5cde9de83a8..3b697a1ee6c 100644
--- a/Flow.Launcher.Core/Plugin/PluginManager.cs
+++ b/Flow.Launcher.Core/Plugin/PluginManager.cs
@@ -133,11 +133,6 @@ public static void InitializePlugins(IPublicAPI api)
}
}
- public static void InstallPlugin(string path)
- {
- PluginInstaller.Install(path);
- }
-
public static List ValidPluginsForQuery(Query query)
{
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))
diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs
index 1df6f2a049b..64b949cbbd2 100644
--- a/Flow.Launcher.Core/Resource/Internationalization.cs
+++ b/Flow.Launcher.Core/Resource/Internationalization.cs
@@ -88,7 +88,6 @@ public void ChangeLanguage(Language language)
{
language = language.NonNull();
- Settings.Language = language.LanguageCode;
RemoveOldLanguageFiles();
if (language != AvailableLanguages.English)
@@ -96,6 +95,7 @@ public void ChangeLanguage(Language language)
LoadLanguage(language);
}
UpdatePluginMetadataTranslations();
+ Settings.Language = language.LanguageCode;
}
diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs
index 20df23e4012..ce9cf08ca3a 100644
--- a/Flow.Launcher.Core/Updater.cs
+++ b/Flow.Launcher.Core/Updater.cs
@@ -8,7 +8,6 @@
using System.Windows;
using JetBrains.Annotations;
using Squirrel;
-using Newtonsoft.Json;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Infrastructure;
@@ -17,6 +16,7 @@
using System.IO;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
+using System.Text.Json.Serialization;
namespace Flow.Launcher.Core
{
@@ -29,101 +29,80 @@ public Updater(string gitHubRepository)
GitHubRepository = gitHubRepository;
}
- public async Task UpdateApp(IPublicAPI api , bool silentUpdate = true)
+ public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true)
{
- UpdateManager updateManager;
- UpdateInfo newUpdateInfo;
-
- if (!silentUpdate)
- api.ShowMsg("Please wait...", "Checking for new update");
-
try
{
- updateManager = await GitHubUpdateManager(GitHubRepository);
- }
- catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
- {
- Log.Exception($"|Updater.UpdateApp|Please check your connection and proxy settings to api.github.com.", e);
- return;
- }
+ UpdateInfo newUpdateInfo;
+
+ if (!silentUpdate)
+ api.ShowMsg("Please wait...", "Checking for new update");
+
+ using var updateManager = await GitHubUpdateManager(GitHubRepository).ConfigureAwait(false);
+
- try
- {
// UpdateApp CheckForUpdate will return value only if the app is squirrel installed
- newUpdateInfo = await updateManager.CheckForUpdate().NonNull();
- }
- catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
- {
- Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to api.github.com.", e);
- updateManager.Dispose();
- return;
- }
+ newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false);
- var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
- var currentVersion = Version.Parse(Constant.Version);
+ var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
+ var currentVersion = Version.Parse(Constant.Version);
- Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>");
+ Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>");
+
+ if (newReleaseVersion <= currentVersion)
+ {
+ if (!silentUpdate)
+ MessageBox.Show("You already have the latest Flow Launcher version");
+ return;
+ }
- if (newReleaseVersion <= currentVersion)
- {
if (!silentUpdate)
- MessageBox.Show("You already have the latest Flow Launcher version");
- updateManager.Dispose();
- return;
- }
+ api.ShowMsg("Update found", "Updating...");
- if (!silentUpdate)
- api.ShowMsg("Update found", "Updating...");
+ await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
- try
- {
- await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply);
- }
- catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
- {
- Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
- updateManager.Dispose();
- return;
- }
-
- await updateManager.ApplyReleases(newUpdateInfo);
+ await updateManager.ApplyReleases(newUpdateInfo).ConfigureAwait(false);
- if (DataLocation.PortableDataLocationInUse())
- {
- var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
- FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
- if (!FilesFolders.VerifyBothFolderFilesEqual(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
- {
- await updateManager.CreateUninstallerRegistryEntry();
- }
+ if (DataLocation.PortableDataLocationInUse())
+ {
+ var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
+ FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
+ if (!FilesFolders.VerifyBothFolderFilesEqual(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
+ {
+ await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false);
+ }
- var newVersionTips = NewVersinoTips(newReleaseVersion.ToString());
-
- Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
+ var newVersionTips = NewVersinoTips(newReleaseVersion.ToString());
- // always dispose UpdateManager
- updateManager.Dispose();
+ Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
- if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ {
+ UpdateManager.RestartApp(Constant.ApplicationFileName);
+ }
+ }
+ catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
{
- UpdateManager.RestartApp(Constant.ApplicationFileName);
+ Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
+ api.ShowMsg("Update Failed", "Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com.");
+ return;
}
}
[UsedImplicitly]
private class GithubRelease
{
- [JsonProperty("prerelease")]
+ [JsonPropertyName("prerelease")]
public bool Prerelease { get; [UsedImplicitly] set; }
- [JsonProperty("published_at")]
+ [JsonPropertyName("published_at")]
public DateTime PublishedAt { get; [UsedImplicitly] set; }
- [JsonProperty("html_url")]
+ [JsonPropertyName("html_url")]
public string HtmlUrl { get; [UsedImplicitly] set; }
}
@@ -133,13 +112,13 @@ private async Task GitHubUpdateManager(string repository)
var uri = new Uri(repository);
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases";
- var json = await Http.Get(api);
+ var jsonStream = await Http.GetStreamAsync(api).ConfigureAwait(false);
- var releases = JsonConvert.DeserializeObject>(json);
+ var releases = await System.Text.Json.JsonSerializer.DeserializeAsync>(jsonStream).ConfigureAwait(false);
var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First();
var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/");
- var client = new WebClient { Proxy = Http.WebProxy() };
+ var client = new WebClient { Proxy = Http.WebProxy };
var downloader = new FileDownloader(client);
var manager = new UpdateManager(latestUrl, urlDownloader: downloader);
diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs
index df146404882..3dba35f8dff 100644
--- a/Flow.Launcher.Infrastructure/Constant.cs
+++ b/Flow.Launcher.Infrastructure/Constant.cs
@@ -35,5 +35,7 @@ public static class Constant
public const string DefaultTheme = "Darker";
public const string Themes = "Themes";
+
+ public const string Website = "https://flow-launcher.github.io";
}
}
diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs
index b7d274205ad..8e2832690e4 100644
--- a/Flow.Launcher.Infrastructure/Http/Http.cs
+++ b/Flow.Launcher.Infrastructure/Http/Http.cs
@@ -6,6 +6,8 @@
using JetBrains.Annotations;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
+using System;
+using System.ComponentModel;
namespace Flow.Launcher.Infrastructure.Http
{
@@ -13,6 +15,14 @@ public static class Http
{
private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko";
+ private static HttpClient client;
+
+ private static SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler()
+ {
+ UseProxy = true,
+ Proxy = WebProxy
+ };
+
static Http()
{
// need to be added so it would work on a win10 machine
@@ -20,64 +30,103 @@ static Http()
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
+
+ client = new HttpClient(socketsHttpHandler, false);
+ client.DefaultRequestHeaders.Add("User-Agent", UserAgent);
}
- public static HttpProxy Proxy { private get; set; }
- public static IWebProxy WebProxy()
+ private static HttpProxy proxy;
+
+ public static HttpProxy Proxy
{
- if (Proxy != null && Proxy.Enabled && !string.IsNullOrEmpty(Proxy.Server))
+ private get { return proxy; }
+ set
{
- if (string.IsNullOrEmpty(Proxy.UserName) || string.IsNullOrEmpty(Proxy.Password))
- {
- var webProxy = new WebProxy(Proxy.Server, Proxy.Port);
- return webProxy;
- }
- else
+ proxy = value;
+ proxy.PropertyChanged += UpdateProxy;
+ }
+ }
+
+ public static WebProxy WebProxy { get; } = new WebProxy();
+
+ ///
+ /// Update the Address of the Proxy to modify the client Proxy
+ ///
+ public static void UpdateProxy(ProxyProperty property)
+ {
+ (WebProxy.Address, WebProxy.Credentials) = property switch
+ {
+ ProxyProperty.Enabled => Proxy.Enabled switch
{
- var webProxy = new WebProxy(Proxy.Server, Proxy.Port)
+ true => Proxy.UserName switch
{
- Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password)
- };
- return webProxy;
- }
+ var userName when !string.IsNullOrEmpty(userName) =>
+ (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null),
+ _ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"),
+ new NetworkCredential(Proxy.UserName, Proxy.Password))
+ },
+ false => (null, null)
+ },
+ ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
+ 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()
+ };
+ }
+
+ public static async Task Download([NotNull] string url, [NotNull] string filePath)
+ {
+ using var response = await client.GetAsync(url);
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+ await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
+ await response.Content.CopyToAsync(fileStream);
}
else
{
- return WebRequest.GetSystemWebProxy();
+ throw new HttpRequestException($"Error code <{response.StatusCode}> returned from <{url}>");
}
}
- public static void Download([NotNull] string url, [NotNull] string filePath)
+ ///
+ /// Asynchrously get the result as string from url.
+ /// When supposing the result is long and large, try using GetStreamAsync to avoid reading as string
+ ///
+ ///
+ ///
+ public static Task GetAsync([NotNull] string url)
{
- var client = new WebClient { Proxy = WebProxy() };
- client.Headers.Add("user-agent", UserAgent);
- client.DownloadFile(url, filePath);
+ Log.Debug($"|Http.Get|Url <{url}>");
+ return GetAsync(new Uri(url.Replace("#", "%23")));
}
- public static async Task Get([NotNull] string url, string encoding = "UTF-8")
+ public static async Task GetAsync([NotNull] Uri url)
{
Log.Debug($"|Http.Get|Url <{url}>");
- var request = WebRequest.CreateHttp(url);
- request.Method = "GET";
- request.Timeout = 1000;
- request.Proxy = WebProxy();
- request.UserAgent = UserAgent;
- var response = await request.GetResponseAsync() as HttpWebResponse;
- response = response.NonNull();
- var stream = response.GetResponseStream().NonNull();
-
- using (var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)))
+ using var response = await client.GetAsync(url);
+ var content = await response.Content.ReadAsStringAsync();
+ if (response.StatusCode == HttpStatusCode.OK)
{
- var content = await reader.ReadToEndAsync();
- if (response.StatusCode == HttpStatusCode.OK)
- {
- return content;
- }
- else
- {
- throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
- }
+ return content;
}
+ else
+ {
+ throw new HttpRequestException(
+ $"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
+ }
+ }
+
+ ///
+ /// Asynchrously get the result as stream from url.
+ ///
+ ///
+ ///
+ public static async Task GetStreamAsync([NotNull] string url)
+ {
+ Log.Debug($"|Http.Get|Url <{url}>");
+ var response = await client.GetAsync(url);
+ return await response.Content.ReadAsStreamAsync();
}
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs
index 80c6684f55c..b1c09024f25 100644
--- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs
+++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs
@@ -26,7 +26,7 @@ public class ImageCache
private const int MaxCached = 50;
public ConcurrentDictionary Data { get; private set; } = new ConcurrentDictionary();
private const int permissibleFactor = 2;
-
+
public void Initialization(Dictionary usage)
{
foreach (var key in usage.Keys)
@@ -44,14 +44,14 @@ public ImageSource this[string path]
value.usage++;
return value.imageSource;
}
-
+
return null;
}
set
{
Data.AddOrUpdate(
- path,
- new ImageUsage(0, value),
+ path,
+ new ImageUsage(0, value),
(k, v) =>
{
v.imageSource = value;
@@ -65,22 +65,15 @@ public ImageSource this[string path]
if (Data.Count > permissibleFactor * MaxCached)
{
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
-
-
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
- {
- if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
- {
- Data.TryRemove(key, out _);
- }
- }
+ Data.TryRemove(key, out _);
}
}
}
public bool ContainsKey(string key)
{
- var contains = Data.ContainsKey(key);
+ var contains = Data.ContainsKey(key) && Data[key] != null;
return contains;
}
@@ -97,5 +90,4 @@ public int UniqueImagesInCache()
return Data.Values.Select(x => x.imageSource).Distinct().Count();
}
}
-
}
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
index edfb88cbfe6..ac333d567b8 100644
--- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
+++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
@@ -18,6 +18,8 @@ public static class ImageLoader
private static readonly ConcurrentDictionary GuidToKey = new ConcurrentDictionary();
private static IImageHashGenerator _hashGenerator;
private static bool EnableImageHash = true;
+ public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
+
private static readonly string[] ImageExtensions =
{
@@ -61,7 +63,7 @@ public static void Save()
{
lock (_storage)
{
- _storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage));
+ _storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, x => x.usage));
}
}
@@ -211,6 +213,11 @@ private static BitmapSource GetThumbnail(string path, ThumbnailOptions option =
option);
}
+ public static bool CacheContainImage(string path)
+ {
+ return ImageCache.ContainsKey(path) && ImageCache[path] != null;
+ }
+
public static ImageSource Load(string path, bool loadFullImage = false)
{
var imageResult = LoadInternal(path, loadFullImage);
@@ -221,7 +228,7 @@ public static ImageSource Load(string path, bool loadFullImage = false)
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
if (hash != null)
{
-
+
if (GuidToKey.TryGetValue(hash, out string key))
{ // image already exists
img = ImageCache[key] ?? img;
diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs
index 38f1ab879c1..80fd1282035 100644
--- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs
+++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs
@@ -1,14 +1,10 @@
using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
-using Flow.Launcher.Infrastructure.Logger;
-using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings;
using ToolGood.Words.Pinyin;
-using System.Threading.Tasks;
namespace Flow.Launcher.Infrastructure
{
@@ -27,7 +23,6 @@ public void Initialize([NotNull] Settings settings)
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
-
public string Translate(string content)
{
if (_settings.ShouldUsePinyin)
@@ -36,10 +31,40 @@ public string Translate(string content)
{
if (WordsHelper.HasChinese(content))
{
- var result = WordsHelper.GetPinyin(content, ";");
- result = GetFirstPinyinChar(result) + result.Replace(";", "");
- _pinyinCache[content] = result;
- return result;
+ var resultList = WordsHelper.GetPinyinList(content);
+
+ StringBuilder resultBuilder = new StringBuilder();
+
+ for (int i = 0; i < resultList.Length; i++)
+ {
+ if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
+ resultBuilder.Append(resultList[i].First());
+ }
+
+ resultBuilder.Append(' ');
+
+ bool pre = false;
+
+ for (int i = 0; i < resultList.Length; i++)
+ {
+ if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
+ {
+ resultBuilder.Append(' ');
+ resultBuilder.Append(resultList[i]);
+ pre = true;
+ }
+ else
+ {
+ if (pre)
+ {
+ pre = false;
+ resultBuilder.Append(' ');
+ }
+ resultBuilder.Append(resultList[i]);
+ }
+ }
+
+ return _pinyinCache[content] = resultBuilder.ToString();
}
else
{
@@ -56,10 +81,5 @@ public string Translate(string content)
return content;
}
}
-
- private string GetFirstPinyinChar(string content)
- {
- return string.Concat(content.Split(';').Select(x => x.First()));
- }
}
}
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/UserSettings/HttpProxy.cs b/Flow.Launcher.Infrastructure/UserSettings/HttpProxy.cs
index c1b0c1dd7fe..21319352633 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/HttpProxy.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/HttpProxy.cs
@@ -1,11 +1,80 @@
-namespace Flow.Launcher.Infrastructure.UserSettings
+using System.ComponentModel;
+
+namespace Flow.Launcher.Infrastructure.UserSettings
{
+ public enum ProxyProperty
+ {
+ Enabled,
+ Server,
+ Port,
+ UserName,
+ Password
+ }
+
public class HttpProxy
{
- public bool Enabled { get; set; } = false;
- public string Server { get; set; }
- public int Port { get; set; }
- public string UserName { get; set; }
- public string Password { get; set; }
+ private bool _enabled = false;
+ private string _server;
+ private int _port;
+ private string _userName;
+ private string _password;
+
+ public bool Enabled
+ {
+ get => _enabled;
+ set
+ {
+ _enabled = value;
+ OnPropertyChanged(ProxyProperty.Enabled);
+ }
+ }
+
+ public string Server
+ {
+ get => _server;
+ set
+ {
+ _server = value;
+ OnPropertyChanged(ProxyProperty.Server);
+ }
+ }
+
+ public int Port
+ {
+ get => _port;
+ set
+ {
+ _port = value;
+ OnPropertyChanged(ProxyProperty.Port);
+ }
+ }
+
+ public string UserName
+ {
+ get => _userName;
+ set
+ {
+ _userName = value;
+ OnPropertyChanged(ProxyProperty.UserName);
+ }
+ }
+
+ public string Password
+ {
+ get => _password;
+ set
+ {
+ _password = value;
+ OnPropertyChanged(ProxyProperty.Password);
+ }
+ }
+
+ public delegate void ProxyPropertyChangedHandler(ProxyProperty property);
+ public event ProxyPropertyChangedHandler PropertyChanged;
+
+ private void OnPropertyChanged(ProxyProperty property)
+ {
+ PropertyChanged?.Invoke(property);
+ }
}
}
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
index 837fe3b7164..832b6fbfaf2 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
@@ -9,10 +9,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
public class Settings : BaseModel
{
+ private string language = "en";
+
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
public string OpenResultModifiers { get; set; } = KeyConstant.Alt;
public bool ShowOpenResultHotkey { get; set; } = true;
- public string Language { get; set; } = "en";
+ public string Language
+ {
+ get => language; set {
+ language = value;
+ OnPropertyChanged();
+ }
+ }
public string Theme { get; set; } = Constant.DefaultTheme;
public bool UseDropShadowEffect { get; set; } = false;
public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name;
diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
index 0f6450d1899..70013c2740f 100644
--- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
+++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
@@ -14,10 +14,10 @@
- 1.3.0
- 1.3.0
- 1.3.0
- 1.3.0
+ 1.3.1
+ 1.3.1
+ 1.3.1
+ 1.3.1
Flow.Launcher.Plugin
Flow-Launcher
MIT
diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs
index 68197390509..ccc00d5e938 100644
--- a/Flow.Launcher.Plugin/IPublicAPI.cs
+++ b/Flow.Launcher.Plugin/IPublicAPI.cs
@@ -63,12 +63,6 @@ public interface IPublicAPI
///
void OpenSettingDialog();
- ///
- /// Install Flow Launcher plugin
- ///
- /// Plugin path (ends with .flowlauncher)
- void InstallPlugin(string path);
-
///
/// Get translation of current language
/// You need to implement IPluginI18n if you want to support multiple languages for your plugin
diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln
index 6196aa5df1f..21c3b47dc0a 100644
--- a/Flow.Launcher.sln
+++ b/Flow.Launcher.sln
@@ -15,23 +15,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc
ProjectSection(ProjectDependencies) = postProject
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0}
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {F35190AA-4758-4D9E-A193-E3BDF6AD3567}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
- {049490F0-ECD2-4148-9B39-2135EC346EBE} = {049490F0-ECD2-4148-9B39-2135EC346EBE}
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginManagement", "Plugins\Flow.Launcher.Plugin.PluginManagement\Flow.Launcher.Plugin.PluginManagement.csproj", "{049490F0-ECD2-4148-9B39-2135EC346EBE}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Core", "Flow.Launcher.Core\Flow.Launcher.Core.csproj", "{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Program", "Plugins\Flow.Launcher.Plugin.Program\Flow.Launcher.Plugin.Program.csproj", "{FDB3555B-58EF-4AE6-B5F1-904719637AB4}"
@@ -46,8 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Sys",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Url", "Plugins\Flow.Launcher.Plugin.Url\Flow.Launcher.Plugin.Url.csproj", "{A3DCCBCA-ACC1-421D-B16E-210896234C26}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Color", "Plugins\Flow.Launcher.Plugin.Color\Flow.Launcher.Plugin.Color.csproj", "{F35190AA-4758-4D9E-A193-E3BDF6AD3567}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FFD651C7-0546-441F-BC8C-D4EE8FD01EA7}"
ProjectSection(SolutionItems) = preProject
.gitattributes = .gitattributes
@@ -71,6 +66,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Explor
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.ProcessKiller", "Plugins\Flow.Launcher.Plugin.ProcessKiller\Flow.Launcher.Plugin.ProcessKiller.csproj", "{588088F4-3262-4F9F-9663-A05DE12534C3}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginsManager", "Plugins\Flow.Launcher.Plugin.PluginsManager\Flow.Launcher.Plugin.PluginsManager.csproj", "{4792A74A-0CEA-4173-A8B2-30E6764C6217}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -129,18 +126,6 @@ Global
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.Build.0 = Release|Any CPU
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = Release|Any CPU
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.Build.0 = Release|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.ActiveCfg = Debug|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.Build.0 = Debug|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.ActiveCfg = Debug|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.Build.0 = Debug|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.Build.0 = Release|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.ActiveCfg = Release|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.Build.0 = Release|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.ActiveCfg = Release|Any CPU
- {049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.Build.0 = Release|Any CPU
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -226,18 +211,6 @@ Global
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x64.Build.0 = Release|Any CPU
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.ActiveCfg = Release|Any CPU
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.Build.0 = Release|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x64.ActiveCfg = Debug|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x64.Build.0 = Debug|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x86.Build.0 = Debug|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|Any CPU.Build.0 = Release|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.ActiveCfg = Release|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.Build.0 = Release|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.ActiveCfg = Release|Any CPU
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.Build.0 = Release|Any CPU
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -298,24 +271,35 @@ Global
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.Build.0 = Release|Any CPU
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.ActiveCfg = Release|Any CPU
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.Build.0 = Release|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.Build.0 = Debug|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.Build.0 = Debug|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.ActiveCfg = Release|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.Build.0 = Release|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.ActiveCfg = Release|Any CPU
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {049490F0-ECD2-4148-9B39-2135EC346EBE} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
+ {4792A74A-0CEA-4173-A8B2-30E6764C6217} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}
diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml
index 8b9487b21f4..bf001d507c5 100644
--- a/Flow.Launcher/Languages/sk.xaml
+++ b/Flow.Launcher/Languages/sk.xaml
@@ -5,8 +5,8 @@
Nepodarilo sa registrovať klávesovú skratku {0}
Nepodarilo sa spustiť {0}
Neplatný formát súboru pre plugin Flow Launchera
- Pri tomto dopyte umiestniť navrchu
- Zrušiť umiestnenie navrchu pri tomto dopyte
+ Pri tomto zadaní umiestniť navrchu
+ Zrušiť umiestnenie navrchu pri tomto zadaní
Spustiť dopyt: {0}
Posledný čas realizácie: {0}
Otvoriť
@@ -22,7 +22,7 @@
Nezobrazovať upozornenia na novú verziu
Zapamätať si posledné umiestnenie
Jazyk
- Posledný dopyt
+ Posledné vyhľadávanie
Ponechať
Označiť
Vymazať
@@ -34,7 +34,7 @@
Schovať Flow Launcher po spustení
Schovať ikonu z oblasti oznámení
Presnosť vyhľadávania
- Dá sa použiť Pinyin
+ Použiť Pinyin
Plugin
@@ -96,11 +96,11 @@
Verzia
Flow Launcher bol aktivovaný {0}-krát
Skontrolovať aktualizácie
- Je dostupná nová verzia {0}, prosím, reštartujte Flow Launcher.
+ Je dostupná nová verzia {0}, chcete reštartovať Flow Launcher, aby sa mohol aktualizovať?
Kontrola aktualizácií zlyhala, prosím, skontrolujte pripojenie na internet a nastavenie proxy k api.github.com.
Sťahovanie aktualizácií zlyhalo, skontrolujte pripojenie na internet a nastavenie proxy k github-cloud.s3.amazonaws.com,
- alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácií.
+ alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácie.
Poznámky k vydaniu
diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml
index 0cc671ef65a..07bb9633903 100644
--- a/Flow.Launcher/MainWindow.xaml
+++ b/Flow.Launcher/MainWindow.xaml
@@ -22,7 +22,6 @@
Loaded="OnLoaded"
Initialized="OnInitialized"
Closing="OnClosing"
- Drop="OnDrop"
LocationChanged="OnLocationChanged"
Deactivated="OnDeactivated"
PreviewKeyDown="OnKeyDown"
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 2c0beaaabcd..3812b4e1f0a 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -52,12 +52,14 @@ private void OnClosing(object sender, CancelEventArgs e)
private void OnInitialized(object sender, EventArgs e)
{
- // show notify icon when flowlauncher is hided
- InitializeNotifyIcon();
+
}
private void OnLoaded(object sender, RoutedEventArgs _)
{
+ // show notify icon when flowlauncher is hidden
+ InitializeNotifyIcon();
+
// todo is there a way to set blur only once?
ThemeManager.Instance.SetBlurForWindow();
WindowsInteropHelper.DisableControlBox(this);
@@ -87,11 +89,17 @@ private void OnLoaded(object sender, RoutedEventArgs _)
};
_settings.PropertyChanged += (o, e) =>
{
- if (e.PropertyName == nameof(Settings.HideNotifyIcon))
+ switch (e.PropertyName)
{
- _notifyIcon.Visible = !_settings.HideNotifyIcon;
+ case nameof(Settings.HideNotifyIcon):
+ _notifyIcon.Visible = !_settings.HideNotifyIcon;
+ break;
+ case nameof(Settings.Language):
+ UpdateNotifyIconText();
+ break;
}
};
+
InitializePosition();
}
@@ -103,6 +111,18 @@ private void InitializePosition()
_settings.WindowLeft = Left;
}
+ private void UpdateNotifyIconText()
+ {
+ var menu = _notifyIcon.ContextMenuStrip;
+ var open = menu.Items[0];
+ var setting = menu.Items[1];
+ var exit = menu.Items[2];
+
+ open.Text = InternationalizationManager.Instance.GetTranslation("iconTrayOpen");
+ setting.Text = InternationalizationManager.Instance.GetTranslation("iconTraySettings");
+ exit.Text = InternationalizationManager.Instance.GetTranslation("iconTrayExit");
+ }
+
private void InitializeNotifyIcon()
{
_notifyIcon = new NotifyIcon
@@ -179,25 +199,6 @@ private void OnPreviewMouseButtonDown(object sender, MouseButtonEventArgs e)
}
}
-
- private void OnDrop(object sender, DragEventArgs e)
- {
- if (e.Data.GetDataPresent(DataFormats.FileDrop))
- {
- // Note that you can have more than one file.
- string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
- if (files[0].ToLower().EndsWith(".flowlauncher"))
- {
- PluginManager.InstallPlugin(files[0]);
- }
- else
- {
- MessageBox.Show(InternationalizationManager.Instance.GetTranslation("invalidFlowLauncherPluginFileFormat"));
- }
- }
- e.Handled = false;
- }
-
private void OnPreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
@@ -294,7 +295,5 @@ private void OnTextChanged(object sender, TextChangedEventArgs e)
_viewModel.QueryTextCursorMovedToEnd = false;
}
}
-
-
}
}
\ No newline at end of file
diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs
index 0cc5a0e5d1f..90d4fff63e8 100644
--- a/Flow.Launcher/PublicAPIInstance.cs
+++ b/Flow.Launcher/PublicAPIInstance.cs
@@ -115,11 +115,6 @@ public void StopLoadingBar()
_mainVM.ProgressBarVisibility = Visibility.Collapsed;
}
- public void InstallPlugin(string path)
- {
- Application.Current.Dispatcher.Invoke(() => PluginManager.InstallPlugin(path));
- }
-
public string GetTranslation(string key)
{
return InternationalizationManager.Instance.GetTranslation(key);
diff --git a/Flow.Launcher/ResultListBox.xaml b/Flow.Launcher/ResultListBox.xaml
index 3280dc457a1..072196605a6 100644
--- a/Flow.Launcher/ResultListBox.xaml
+++ b/Flow.Launcher/ResultListBox.xaml
@@ -42,7 +42,7 @@
+ Source="{Binding Image.Value}" />
diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml
index 32f9e9a6e8c..e47f0e7791f 100644
--- a/Flow.Launcher/SettingWindow.xaml
+++ b/Flow.Launcher/SettingWindow.xaml
@@ -429,8 +429,8 @@
-
-
+
+
diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs
index a4fe2ede4fc..00a0e1ae562 100644
--- a/Flow.Launcher/ViewModel/ResultViewModel.cs
+++ b/Flow.Launcher/ViewModel/ResultViewModel.cs
@@ -1,23 +1,66 @@
using System;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
-using System.Windows.Threading;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
-
namespace Flow.Launcher.ViewModel
{
public class ResultViewModel : BaseModel
{
+ public class LazyAsync : Lazy>
+ {
+ private T defaultValue;
+
+ private readonly Action _updateCallback;
+ public new T Value
+ {
+ get
+ {
+ if (!IsValueCreated)
+ {
+ base.Value.ContinueWith(_ =>
+ {
+ _updateCallback();
+ });
+
+ return defaultValue;
+ }
+
+ if (!base.Value.IsCompleted || base.Value.IsFaulted)
+ return defaultValue;
+
+ return base.Value.Result;
+ }
+ }
+ public LazyAsync(Func> factory, T defaultValue, Action updateCallback) : base(factory)
+ {
+ if (defaultValue != null)
+ {
+ this.defaultValue = defaultValue;
+ }
+
+ _updateCallback = updateCallback;
+ }
+ }
+
public ResultViewModel(Result result, Settings settings)
{
if (result != null)
{
Result = result;
+
+ Image = new LazyAsync(
+ SetImage,
+ ImageLoader.DefaultImage,
+ () =>
+ {
+ OnPropertyChanged(nameof(Image));
+ });
}
Settings = settings;
@@ -25,39 +68,45 @@ public ResultViewModel(Result result, Settings settings)
public Settings Settings { get; private set; }
- public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
+ public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
public string OpenResultModifiers => Settings.OpenResultModifiers;
public string ShowTitleToolTip => string.IsNullOrEmpty(Result.TitleToolTip)
- ? Result.Title
+ ? Result.Title
: Result.TitleToolTip;
public string ShowSubTitleToolTip => string.IsNullOrEmpty(Result.SubTitleToolTip)
- ? Result.SubTitle
+ ? Result.SubTitle
: Result.SubTitleToolTip;
- public ImageSource Image
+ public LazyAsync Image { get; set; }
+
+ private async Task SetImage()
{
- get
+ var imagePath = Result.IcoPath;
+ if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
{
- var imagePath = Result.IcoPath;
- if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
+ try
{
- try
- {
- return Result.Icon();
- }
- catch (Exception e)
- {
- Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
- imagePath = Constant.MissingImgIcon;
- }
+ return Result.Icon();
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
+ imagePath = Constant.MissingImgIcon;
}
-
+ }
+
+ if (ImageLoader.CacheContainImage(imagePath))
+ {
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
return ImageLoader.Load(imagePath);
}
+ else
+ {
+ return await Task.Run(() => ImageLoader.Load(imagePath));
+ }
}
public Result Result { get; }
@@ -84,6 +133,5 @@ public override string ToString()
{
return Result.ToString();
}
-
}
}
diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
index 853925852a1..c122f8037d1 100644
--- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
+++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
@@ -213,7 +213,7 @@ public string TestProxy()
#region plugin
- public static string Plugin => "http://www.wox.one/plugin";
+ public static string Plugin => @"https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest";
public PluginViewModel SelectedPlugin { get; set; }
public IList PluginViewModels
@@ -450,7 +450,7 @@ public FamilyTypeface SelectedResultFontFaces
#region about
- public string Github => _updater.GitHubRepository;
+ public string Website => Constant.Website;
public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest";
public static string Version => Constant.Version;
public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes);
diff --git a/JsonRPC/wox.py b/JsonRPC/wox.py
deleted file mode 100644
index 1beaa1d7e9c..00000000000
--- a/JsonRPC/wox.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import print_function
-import json
-import sys
-import inspect
-
-class FlowLauncher(object):
- """
- Flow.Launcher python plugin base
- """
-
- def __init__(self):
- rpc_request = json.loads(sys.argv[1])
- # proxy is not working now
- self.proxy = rpc_request.get("proxy",{})
- request_method_name = rpc_request.get("method")
- request_parameters = rpc_request.get("parameters")
- methods = inspect.getmembers(self, predicate=inspect.ismethod)
-
- request_method = dict(methods)[request_method_name]
- results = request_method(*request_parameters)
-
- if request_method_name == "query" or request_method_name == "context_menu":
- print(json.dumps({"result": results}))
-
- def query(self,query):
- """
- sub class need to override this method
- """
- return []
-
- def context_menu(self, data):
- """
- optional context menu entries for a result
- """
- return []
-
- def debug(self,msg):
- """
- alert msg
- """
- print("DEBUG:{}".format(msg))
- sys.exit()
-
-class FlowLauncherAPI(object):
-
- @classmethod
- def change_query(cls,query,requery = False):
- """
- change flowlauncher query
- """
- print(json.dumps({"method": "Flow.Launcher.ChangeQuery","parameters":[query,requery]}))
-
- @classmethod
- def shell_run(cls,cmd):
- """
- run shell commands
- """
- print(json.dumps({"method": "Flow.Launcher.ShellRun","parameters":[cmd]}))
-
- @classmethod
- def close_app(cls):
- """
- close flowlauncher
- """
- print(json.dumps({"method": "Flow.Launcher.CloseApp","parameters":[]}))
-
- @classmethod
- def hide_app(cls):
- """
- hide flowlauncher
- """
- print(json.dumps({"method": "Flow.Launcher.HideApp","parameters":[]}))
-
- @classmethod
- def show_app(cls):
- """
- show flowlauncher
- """
- print(json.dumps({"method": "Flow.Launcher.ShowApp","parameters":[]}))
-
- @classmethod
- def show_msg(cls,title,sub_title,ico_path=""):
- """
- show messagebox
- """
- print(json.dumps({"method": "Flow.Launcher.ShowMsg","parameters":[title,sub_title,ico_path]}))
-
- @classmethod
- def open_setting_dialog(cls):
- """
- open setting dialog
- """
- print(json.dumps({"method": "Flow.Launcher.OpenSettingDialog","parameters":[]}))
-
- @classmethod
- def start_loadingbar(cls):
- """
- start loading animation in flowlauncher
- """
- print(json.dumps({"method": "Flow.Launcher.StartLoadingBar","parameters":[]}))
-
- @classmethod
- def stop_loadingbar(cls):
- """
- stop loading animation in flowlauncher
- """
- print(json.dumps({"method": "Flow.Launcher.StopLoadingBar","parameters":[]}))
-
- @classmethod
- def reload_plugins(cls):
- """
- reload all flowlauncher plugins
- """
- print(json.dumps({"method": "Flow.Launcher.ReloadPlugins","parameters":[]}))
diff --git a/LICENSE b/LICENSE
index cb4b563c0a7..8be14c31de1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,6 @@
The MIT License (MIT)
+Copyright (c) 2019 Flow-Launcher
Copyright (c) 2015 Wox
Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml
index dd52d527918..c08f0265c05 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml
@@ -12,5 +12,5 @@
Použiť podľa systému
Čiarka (,)
Bodka (.)
- Max. desatinných miest
+ Desatinné miesta
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json
index 5ec1ac00295..709757d1a09 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json
@@ -4,7 +4,7 @@
"Name": "Calculator",
"Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)",
"Author": "cxfksword",
- "Version": "1.1.2",
+ "Version": "1.1.3",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj b/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj
deleted file mode 100644
index c7fe8271a6c..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
- Library
- netcoreapp3.1
- {F35190AA-4758-4D9E-A193-E3BDF6AD3567}
- Properties
- Flow.Launcher.Plugin.Color
- Flow.Launcher.Plugin.Color
- true
- false
- false
-
-
-
- true
- full
- false
- ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Color\
- DEBUG;TRACE
- prompt
- 4
- false
-
-
-
- pdbonly
- true
- ..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Color\
- TRACE
- prompt
- 4
- false
-
-
-
-
- PreserveNewest
-
-
-
-
-
- PreserveNewest
-
-
-
-
-
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Images/color.png b/Plugins/Flow.Launcher.Plugin.Color/Images/color.png
deleted file mode 100644
index da28583b1c9..00000000000
Binary files a/Plugins/Flow.Launcher.Plugin.Color/Images/color.png and /dev/null differ
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/de.xaml
deleted file mode 100644
index 3244dee145f..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Languages/de.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Farben
- Stellt eine HEX-Farben Vorschau bereit. (Versuche #000 in Flow Launcher)
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/en.xaml
deleted file mode 100644
index 85e2830dbbe..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Languages/en.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Colors
- Allows to preview colors using hex values.(Try #000 in Flow Launcher)
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/pl.xaml
deleted file mode 100644
index 15525cfe96a..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Languages/pl.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Kolory
- Podgląd kolorów po wpisaniu ich kodu szesnastkowego. (Spróbuj wpisać #000 w oknie Flow Launchera)
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/sk.xaml
deleted file mode 100644
index 4b208691a8f..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Languages/sk.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Farby
- Zobrazuje náhľad farieb v HEX formáte. (Skúste #000 vo Flow Launcheri)
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/tr.xaml
deleted file mode 100644
index f56e7352620..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Languages/tr.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Renkler
- Hex kodunu girdiğiniz renkleri görüntülemeye yarar.(#000 yazmayı deneyin)
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-cn.xaml
deleted file mode 100644
index 39ede4844fb..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-cn.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- 颜色
- 提供在Flow Launcher查询hex颜色。(尝试在Flow Launcher中输入#000)
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-tw.xaml
deleted file mode 100644
index 4e7062a2262..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Languages/zh-tw.xaml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- 顏色
- 提供在 Flow Launcher 查詢 hex 顏色。(試著在 Flow Launcher 中輸入 #000)
-
diff --git a/Plugins/Flow.Launcher.Plugin.Color/Main.cs b/Plugins/Flow.Launcher.Plugin.Color/Main.cs
deleted file mode 100644
index a15483ebc7e..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/Main.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.IO;
-using System.Linq;
-using System.Windows;
-
-namespace Flow.Launcher.Plugin.Color
-{
- public sealed class ColorsPlugin : IPlugin, IPluginI18n
- {
- private string DIR_PATH = Path.Combine(Path.GetTempPath(), @"Plugins\Colors\");
- private PluginInitContext context;
- private const int IMG_SIZE = 32;
-
- private DirectoryInfo ColorsDirectory { get; set; }
-
- public ColorsPlugin()
- {
- if (!Directory.Exists(DIR_PATH))
- {
- ColorsDirectory = Directory.CreateDirectory(DIR_PATH);
- }
- else
- {
- ColorsDirectory = new DirectoryInfo(DIR_PATH);
- }
- }
-
- public List Query(Query query)
- {
- var raw = query.Search;
- if (!IsAvailable(raw)) return new List(0);
- try
- {
- var cached = Find(raw);
- if (cached.Length == 0)
- {
- var path = CreateImage(raw);
- return new List
- {
- new Result
- {
- Title = raw,
- IcoPath = path,
- Action = _ =>
- {
- Clipboard.SetText(raw);
- return true;
- }
- }
- };
- }
- return cached.Select(x => new Result
- {
- Title = raw,
- IcoPath = x.FullName,
- Action = _ =>
- {
- Clipboard.SetText(raw);
- return true;
- }
- }).ToList();
- }
- catch (Exception exception)
- {
- // todo: log
- return new List(0);
- }
- }
-
- private bool IsAvailable(string query)
- {
- // todo: rgb, names
- var length = query.Length - 1; // minus `#` sign
- return query.StartsWith("#") && (length == 3 || length == 6);
- }
-
- public FileInfo[] Find(string name)
- {
- var file = string.Format("{0}.png", name.Substring(1));
- return ColorsDirectory.GetFiles(file, SearchOption.TopDirectoryOnly);
- }
-
- private string CreateImage(string name)
- {
- using (var bitmap = new Bitmap(IMG_SIZE, IMG_SIZE))
- using (var graphics = Graphics.FromImage(bitmap))
- {
- var color = ColorTranslator.FromHtml(name);
- graphics.Clear(color);
-
- var path = CreateFileName(name);
- bitmap.Save(path, ImageFormat.Png);
- return path;
- }
- }
-
- private string CreateFileName(string name)
- {
- return string.Format("{0}{1}.png", ColorsDirectory.FullName, name.Substring(1));
- }
-
- public void Init(PluginInitContext context)
- {
- this.context = context;
- }
-
-
- public string GetTranslatedPluginTitle()
- {
- return context.API.GetTranslation("flowlauncher_plugin_color_plugin_name");
- }
-
- public string GetTranslatedPluginDescription()
- {
- return context.API.GetTranslation("flowlauncher_plugin_color_plugin_description");
- }
- }
-}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Color/plugin.json b/Plugins/Flow.Launcher.Plugin.Color/plugin.json
deleted file mode 100644
index 8c0c483bad9..00000000000
--- a/Plugins/Flow.Launcher.Plugin.Color/plugin.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "ID": "9B36CE6181FC47FBB597AA2C29CD9B0A",
- "ActionKeyword": "*",
- "Name": "Colors",
- "Description": "Provide hex color preview.(Try #000 in Flow Launcher)",
- "Author": "qianlifeng",
- "Version": "1.1.1",
- "Language": "csharp",
- "Website": "https://github.com/Flow-Launcher/Flow.Launcher",
- "ExecuteFileName": "Flow.Launcher.Plugin.Color.dll",
- "IcoPath": "Images\\color.png"
-}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj b/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj
deleted file mode 100644
index 08e89d86125..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
- Library
- netcoreapp3.1
- {049490F0-ECD2-4148-9B39-2135EC346EBE}
- Properties
- Flow.Launcher.Plugin.PluginManagement
- Flow.Launcher.Plugin.PluginManagement
- true
- true
- false
- false
-
-
-
- true
- full
- false
- ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginManagement\
- DEBUG;TRACE
- prompt
- 4
- false
-
-
-
- pdbonly
- true
- ..\..\Output\Release\Plugins\Flow.Launcher.Plugin.PluginManagement\
- TRACE
- prompt
- 4
- false
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
-
-
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
-
- MSBuild:Compile
- Designer
- PreserveNewest
-
-
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.PluginResult.cs b/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.PluginResult.cs
deleted file mode 100644
index 7f5d75d4e98..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.PluginResult.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Flow.Launcher.Plugin.PluginManagement
-{
- public class FlowLauncherPluginResult
- {
- public string plugin_file;
- public string description;
- public int liked_count;
- public string name;
- public string version;
- }
-}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Images/plugin.png b/Plugins/Flow.Launcher.Plugin.PluginManagement/Images/plugin.png
deleted file mode 100644
index 6ff9b8b1576..00000000000
Binary files a/Plugins/Flow.Launcher.Plugin.PluginManagement/Images/plugin.png and /dev/null differ
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/de.xaml
deleted file mode 100644
index 38b2f1b4b3a..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/de.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Flow Launcher Plugin Verwaltung
- Installiere/Entferne/Aktualisiere Flow Launcher Plugins
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/en.xaml
deleted file mode 100644
index b49f33c767e..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/en.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Plugin Management
- Install, remove or update Flow Launcher plugins
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/pl.xaml
deleted file mode 100644
index 362db73e5ed..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/pl.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Zarządzanie wtyczkami Flow Launcher
- Pozwala na instalacje, usuwanie i aktualizacje wtyczek
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/sk.xaml
deleted file mode 100644
index b51eceb6aef..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/sk.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Správca pluginov
- Inštalácia, odinštalácia alebo aktualizácia pluginov Flow Launchera
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/tr.xaml
deleted file mode 100644
index fee82a78bcb..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/tr.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Flow Launcher Eklenti Yöneticisi
- Flow Launcher eklentilerini kurun, kaldırın ya da güncelleyin
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/zh-cn.xaml
deleted file mode 100644
index 009fd976c19..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/zh-cn.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Flow Launcher插件管理
- 安装/卸载/更新Flow Launcher插件
-
-
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/zh-tw.xaml
deleted file mode 100644
index c93d740f133..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Languages/zh-tw.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- Flow Launcher 外掛管理
- 安裝/解除安裝/更新 Flow Launcher 外掛
-
-
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs
deleted file mode 100644
index e1b631517e2..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs
+++ /dev/null
@@ -1,258 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows;
-using Newtonsoft.Json;
-using Flow.Launcher.Infrastructure;
-using Flow.Launcher.Infrastructure.Http;
-using Flow.Launcher.Infrastructure.Logger;
-
-namespace Flow.Launcher.Plugin.PluginManagement
-{
- public class Main : IPlugin, IPluginI18n
- {
- private static string APIBASE = "http://api.wox.one";
- private static string pluginSearchUrl = APIBASE + "/plugin/search/";
- private const string ListCommand = "list";
- private const string InstallCommand = "install";
- private const string UninstallCommand = "uninstall";
- private PluginInitContext context;
-
- public List Query(Query query)
- {
- List results = new List();
-
- if (string.IsNullOrEmpty(query.Search))
- {
- results.Add(ResultForListCommandAutoComplete(query));
- results.Add(ResultForInstallCommandAutoComplete(query));
- results.Add(ResultForUninstallCommandAutoComplete(query));
- return results;
- }
-
- string command = query.FirstSearch.ToLower();
- if (string.IsNullOrEmpty(command)) return results;
-
- if (command == ListCommand)
- {
- return ResultForListInstalledPlugins();
- }
- if (command == UninstallCommand)
- {
- return ResultForUnInstallPlugin(query);
- }
- if (command == InstallCommand)
- {
- return ResultForInstallPlugin(query);
- }
-
- if (InstallCommand.Contains(command))
- {
- results.Add(ResultForInstallCommandAutoComplete(query));
- }
- if (UninstallCommand.Contains(command))
- {
- results.Add(ResultForUninstallCommandAutoComplete(query));
- }
- if (ListCommand.Contains(command))
- {
- results.Add(ResultForListCommandAutoComplete(query));
- }
-
- return results;
- }
-
- private Result ResultForListCommandAutoComplete(Query query)
- {
- string title = ListCommand;
- string subtitle = "list installed plugins";
- return ResultForCommand(query, ListCommand, title, subtitle);
- }
-
- private Result ResultForInstallCommandAutoComplete(Query query)
- {
- string title = $"{InstallCommand} ";
- string subtitle = "list installed plugins";
- return ResultForCommand(query, InstallCommand, title, subtitle);
- }
-
- private Result ResultForUninstallCommandAutoComplete(Query query)
- {
- string title = $"{UninstallCommand} ";
- string subtitle = "list installed plugins";
- return ResultForCommand(query, UninstallCommand, title, subtitle);
- }
-
- private Result ResultForCommand(Query query, string command, string title, string subtitle)
- {
- const string seperater = Plugin.Query.TermSeperater;
- var result = new Result
- {
- Title = title,
- IcoPath = "Images\\plugin.png",
- SubTitle = subtitle,
- Action = e =>
- {
- context.API.ChangeQuery($"{query.ActionKeyword}{seperater}{command}{seperater}");
- return false;
- }
- };
- return result;
- }
-
- private List ResultForInstallPlugin(Query query)
- {
- List results = new List();
- string pluginName = query.SecondSearch;
- if (string.IsNullOrEmpty(pluginName)) return results;
- string json;
- try
- {
- json = Http.Get(pluginSearchUrl + pluginName).Result;
- }
- catch (WebException e)
- {
- //todo happlebao add option in log to decide give user prompt or not
- context.API.ShowMsg("PluginManagement.ResultForInstallPlugin: Can't connect to Wox plugin website, check your conenction");
- Log.Exception("|PluginManagement.ResultForInstallPlugin|Can't connect to Wox plugin website, check your conenction", e);
- return new List();
- }
- List searchedPlugins;
- try
- {
- searchedPlugins = JsonConvert.DeserializeObject>(json);
- }
- catch (JsonSerializationException e)
- {
- context.API.ShowMsg("PluginManagement.ResultForInstallPlugin: Coundn't parse api search results, Please update your Flow Launcher!");
- Log.Exception("|PluginManagement.ResultForInstallPlugin|Coundn't parse api search results, Please update your Flow Launcher!", e);
- return results;
- }
-
- foreach (FlowLauncherPluginResult r in searchedPlugins)
- {
- FlowLauncherPluginResult r1 = r;
- results.Add(new Result
- {
- Title = r.name,
- SubTitle = r.description,
- IcoPath = "Images\\plugin.png",
- TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.name).MatchData,
- SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.description).MatchData,
- Action = c =>
- {
- MessageBoxResult result = MessageBox.Show("Are you sure you wish to install the \'" + r.name + "\' plugin",
- "Install plugin", MessageBoxButton.YesNo);
-
- if (result == MessageBoxResult.Yes)
- {
- string folder = Path.Combine(Path.GetTempPath(), "FlowLauncherPluginDownload");
- if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
- string filePath = Path.Combine(folder, Guid.NewGuid().ToString() + ".flowlauncher");
-
- string pluginUrl = APIBASE + "/media/" + r1.plugin_file;
-
- try
- {
- Http.Download(pluginUrl, filePath);
- }
- catch (WebException e)
- {
- context.API.ShowMsg($"PluginManagement.ResultForInstallPlugin: download failed for <{r.name}>");
- Log.Exception($"|PluginManagement.ResultForInstallPlugin|download failed for <{r.name}>", e);
- return false;
- }
- context.API.InstallPlugin(filePath);
- }
- return false;
- }
- });
- }
- return results;
- }
-
- private List ResultForUnInstallPlugin(Query query)
- {
- List results = new List();
- List allInstalledPlugins = context.API.GetAllPlugins().Select(o => o.Metadata).ToList();
- if (!string.IsNullOrEmpty(query.SecondSearch))
- {
- allInstalledPlugins =
- allInstalledPlugins.Where(o => o.Name.ToLower().Contains(query.SecondSearch.ToLower())).ToList();
- }
-
- foreach (PluginMetadata plugin in allInstalledPlugins)
- {
- results.Add(new Result
- {
- Title = plugin.Name,
- SubTitle = plugin.Description,
- IcoPath = plugin.IcoPath,
- TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Name).MatchData,
- SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Description).MatchData,
- Action = e =>
- {
- UnInstallPlugin(plugin);
- return false;
- }
- });
- }
- return results;
- }
-
- private void UnInstallPlugin(PluginMetadata plugin)
- {
- string content = $"Do you want to uninstall following plugin?{Environment.NewLine}{Environment.NewLine}" +
- $"Name: {plugin.Name}{Environment.NewLine}" +
- $"Version: {plugin.Version}{Environment.NewLine}" +
- $"Author: {plugin.Author}";
- if (MessageBox.Show(content, "Flow Launcher", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
- {
- File.Create(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")).Close();
- var result = MessageBox.Show($"You have uninstalled plugin {plugin.Name} successfully.{Environment.NewLine}" +
- "Restart Flow Launcher to take effect?",
- "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question);
- if (result == MessageBoxResult.Yes)
- {
- context.API.RestartApp();
- }
- }
- }
-
- private List ResultForListInstalledPlugins()
- {
- List results = new List();
- foreach (PluginMetadata plugin in context.API.GetAllPlugins().Select(o => o.Metadata))
- {
- string actionKeywordString = string.Join(" or ", plugin.ActionKeywords.ToArray());
- results.Add(new Result
- {
- Title = $"{plugin.Name} - Action Keywords: {actionKeywordString}",
- SubTitle = plugin.Description,
- IcoPath = plugin.IcoPath
- });
- }
- return results;
- }
-
- public void Init(PluginInitContext context)
- {
- this.context = context;
- }
-
- public string GetTranslatedPluginTitle()
- {
- return context.API.GetTranslation("flowlauncher_plugin_plugin_management_plugin_name");
- }
-
- public string GetTranslatedPluginDescription()
- {
- return context.API.GetTranslation("flowlauncher_plugin_plugin_management_plugin_description");
- }
- }
-}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json
deleted file mode 100644
index 7b8262f795a..00000000000
--- a/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "ID": "D2D2C23B084D422DB66FE0C79D6C2A6A",
- "ActionKeyword": "wpm",
- "Name": "Plugin Management",
- "Description": "Install/Remove/Update Flow Launcher plugins",
- "Author": "qianlifeng",
- "Version": "1.1.1",
- "Language": "csharp",
- "Website": "https://github.com/Flow-Launcher/Flow.Launcher",
- "ExecuteFileName": "Flow.Launcher.Plugin.PluginManagement.dll",
- "IcoPath": "Images\\plugin.png"
-}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs
new file mode 100644
index 00000000000..7bc357be421
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs
@@ -0,0 +1,76 @@
+using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin.PluginsManager.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Flow.Launcher.Plugin.PluginsManager
+{
+ internal class ContextMenu : IContextMenu
+ {
+ private PluginInitContext Context { get; set; }
+
+ public ContextMenu(PluginInitContext context)
+ {
+ Context = context;
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ var pluginManifestInfo = selectedResult.ContextData as UserPlugin;
+
+ return new List
+ {
+ new Result
+ {
+ Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_title"),
+ SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle"),
+ IcoPath = "Images\\website.png",
+ Action = _ =>
+ {
+ SharedCommands.SearchWeb.NewTabInBrowser(pluginManifestInfo.Website);
+ return true;
+ }
+ },
+ new Result
+ {
+ Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_title"),
+ SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_subtitle"),
+ IcoPath = "Images\\sourcecode.png",
+ Action = _ =>
+ {
+ SharedCommands.SearchWeb.NewTabInBrowser(pluginManifestInfo.UrlSourceCode);
+ return true;
+ }
+ },
+ new Result
+ {
+ Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_newissue_title"),
+ SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_newissue_subtitle"),
+ IcoPath = "Images\\request.png",
+ Action = _ =>
+ {
+ // standard UrlSourceCode format in PluginsManifest's plugins.json file: https://github.com/jjw24/WoxDictionary/tree/master
+ var link = pluginManifestInfo.UrlSourceCode.StartsWith("https://github.com")
+ ? pluginManifestInfo.UrlSourceCode.Replace("/tree/master", "/issues/new/choose")
+ : pluginManifestInfo.UrlSourceCode;
+
+ SharedCommands.SearchWeb.NewTabInBrowser(link);
+ return true;
+ }
+ },
+ new Result
+ {
+ Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_title"),
+ SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle"),
+ IcoPath = selectedResult.IcoPath,
+ Action = _ =>
+ {
+ SharedCommands.SearchWeb.NewTabInBrowser("https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest");
+ return true;
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj
new file mode 100644
index 00000000000..cc1a931ce04
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj
@@ -0,0 +1,46 @@
+
+
+
+ Library
+ netcoreapp3.1
+ true
+ true
+ true
+ false
+
+
+
+ ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginsManager
+
+
+
+ ..\..\Output\Release\Plugins\Flow.Launcher.Plugin.PluginsManager
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/pluginsmanager.png b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/pluginsmanager.png
new file mode 100644
index 00000000000..65f0e41dc14
Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/pluginsmanager.png differ
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/request.png b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/request.png
new file mode 100644
index 00000000000..a9126cb9bd8
Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/request.png differ
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/sourcecode.png b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/sourcecode.png
new file mode 100644
index 00000000000..8efbdaa487d
Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/sourcecode.png differ
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/website.png b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/website.png
new file mode 100644
index 00000000000..f96ba15b25c
Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.PluginsManager/Images/website.png differ
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml
new file mode 100644
index 00000000000..8d24c145c45
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml
@@ -0,0 +1,37 @@
+
+
+
+ Downloading plugin
+ Please wait...
+ Successfully downloaded
+ {0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart.
+ {0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart.
+ Plugin Install
+ Plugin Uninstall
+ Install failed: unable to find the plugin.json metadata file from the new plugin
+ No update available
+ All plugins are up to date
+ {0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart.
+ Plugin Update
+ This plugin has an update, would you like to see it?
+ This plugin is already installed
+
+
+
+
+ Plugins Manager
+ Management of installing, uninstalling or updating Flow Launcher plugins
+
+
+ Open website
+ Visit the plugin's website
+ See source code
+ See the plugin's source code
+ Suggest an enhancement or submit an issue
+ Suggest an enhancement or submit an issue to the plugin developer
+ Go to Flow's plugins repository
+ Visit the PluginsManifest repository to see comunity-made plugin submissions
+
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
new file mode 100644
index 00000000000..d700b9dfd27
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
@@ -0,0 +1,91 @@
+using Flow.Launcher.Infrastructure.Storage;
+using Flow.Launcher.Plugin.PluginsManager.ViewModels;
+using Flow.Launcher.Plugin.PluginsManager.Views;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Controls;
+using Flow.Launcher.Infrastructure;
+using System;
+using System.Threading.Tasks;
+
+namespace Flow.Launcher.Plugin.PluginsManager
+{
+ public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n
+ {
+ internal PluginInitContext Context { get; set; }
+
+ internal Settings Settings;
+
+ private SettingsViewModel viewModel;
+
+ private IContextMenu contextMenu;
+
+ internal PluginsManager pluginManager;
+
+ private DateTime lastUpdateTime = DateTime.MinValue;
+
+ public Control CreateSettingPanel()
+ {
+ return new PluginsManagerSettings(viewModel);
+ }
+
+ public void Init(PluginInitContext context)
+ {
+ Context = context;
+ viewModel = new SettingsViewModel(context);
+ Settings = viewModel.Settings;
+ contextMenu = new ContextMenu(Context);
+ pluginManager = new PluginsManager(Context, Settings);
+ lastUpdateTime = DateTime.Now;
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ return contextMenu.LoadContextMenus(selectedResult);
+ }
+
+ public List Query(Query query)
+ {
+ var search = query.Search.ToLower();
+
+ if (string.IsNullOrWhiteSpace(search))
+ return pluginManager.GetDefaultHotKeys();
+
+ if ((DateTime.Now - lastUpdateTime).TotalHours > 12) // 12 hours
+ {
+ Task.Run(async () =>
+ {
+ await pluginManager.UpdateManifest();
+ lastUpdateTime = DateTime.Now;
+ });
+ }
+
+ return search switch
+ {
+ var s when s.StartsWith(Settings.HotKeyInstall) => pluginManager.RequestInstallOrUpdate(s),
+ var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s),
+ var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s),
+ _ => pluginManager.GetDefaultHotKeys().Where(hotkey =>
+ {
+ hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score;
+ return hotkey.Score > 0;
+ }).ToList()
+ };
+ }
+
+ public void Save()
+ {
+ viewModel.Save();
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return Context.API.GetTranslation("plugin_pluginsmanager_plugin_name");
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description");
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs
new file mode 100644
index 00000000000..814e0764df7
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs
@@ -0,0 +1,36 @@
+using Flow.Launcher.Infrastructure.Http;
+using Flow.Launcher.Infrastructure.Logger;
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace Flow.Launcher.Plugin.PluginsManager.Models
+{
+ internal class PluginsManifest
+ {
+ internal List UserPlugins { get; private set; }
+
+ internal PluginsManifest()
+ {
+ Task.Run(async () => await DownloadManifest()).Wait();
+ }
+
+ internal async Task DownloadManifest()
+ {
+ try
+ {
+ await using var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json")
+ .ConfigureAwait(false);
+
+ UserPlugins = await JsonSerializer.DeserializeAsync>(jsonStream).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ Log.Exception("|PluginManagement.GetManifest|Encountered error trying to download plugins manifest", e);
+
+ UserPlugins = new List();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/UserPlugin.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/UserPlugin.cs
new file mode 100644
index 00000000000..c1af3014bf9
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/UserPlugin.cs
@@ -0,0 +1,16 @@
+
+namespace Flow.Launcher.Plugin.PluginsManager.Models
+{
+ public class UserPlugin
+ {
+ public string ID { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public string Author { get; set; }
+ public string Version { get; set; }
+ public string Language { get; set; }
+ public string Website { get; set; }
+ public string UrlDownload { get; set; }
+ public string UrlSourceCode { get; set; }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs
new file mode 100644
index 00000000000..ac15618ca76
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs
@@ -0,0 +1,394 @@
+using Flow.Launcher.Infrastructure;
+using Flow.Launcher.Infrastructure.Http;
+using Flow.Launcher.Infrastructure.Logger;
+using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin.PluginsManager.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace Flow.Launcher.Plugin.PluginsManager
+{
+ internal class PluginsManager
+ {
+ private PluginsManifest pluginsManifest;
+
+ private PluginInitContext Context { get; set; }
+
+ private Settings Settings { get; set; }
+
+ private bool shouldHideWindow = true;
+
+ private bool ShouldHideWindow
+ {
+ set { shouldHideWindow = value; }
+ get
+ {
+ var setValue = shouldHideWindow;
+ // Default value for hide main window is true. Revert after get call.
+ // This ensures when set by another method to false, it is only used once.
+ shouldHideWindow = true;
+
+ return setValue;
+ }
+ }
+
+ private readonly string icoPath = "Images\\pluginsmanager.png";
+
+ internal PluginsManager(PluginInitContext context, Settings settings)
+ {
+ pluginsManifest = new PluginsManifest();
+ Context = context;
+ Settings = settings;
+ }
+
+ internal async Task UpdateManifest()
+ {
+ await pluginsManifest.DownloadManifest();
+ }
+
+ internal List GetDefaultHotKeys()
+ {
+ return new List()
+ {
+ new Result()
+ {
+ Title = Settings.HotKeyInstall,
+ IcoPath = icoPath,
+ Action = _ =>
+ {
+ Context.API.ChangeQuery("pm install ");
+ return false;
+ }
+ },
+ new Result()
+ {
+ Title = Settings.HotkeyUninstall,
+ IcoPath = icoPath,
+ Action = _ =>
+ {
+ Context.API.ChangeQuery("pm uninstall ");
+ return false;
+ }
+ },
+ new Result()
+ {
+ Title = Settings.HotkeyUpdate,
+ IcoPath = icoPath,
+ Action = _ =>
+ {
+ Context.API.ChangeQuery("pm update ");
+ return false;
+ }
+ }
+ };
+ }
+
+ internal async Task InstallOrUpdate(UserPlugin plugin)
+ {
+ if (PluginExists(plugin.ID))
+ {
+ if (Context.API.GetAllPlugins()
+ .Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version.CompareTo(plugin.Version) < 0))
+ {
+ if (MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_update_exists"),
+ Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
+ MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ Context
+ .API
+ .ChangeQuery(
+ $"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.HotkeyUpdate} {plugin.Name}");
+
+ Application.Current.MainWindow.Show();
+ shouldHideWindow = false;
+
+ return;
+ }
+
+ Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_alreadyexists"));
+ return;
+ }
+
+ var message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_prompt"),
+ plugin.Name, plugin.Author,
+ Environment.NewLine, Environment.NewLine);
+
+ if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_install_title"),
+ MessageBoxButton.YesNo) == MessageBoxResult.No)
+ return;
+
+ var filePath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}.zip");
+
+ try
+ {
+ Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
+ Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
+
+ await Http.Download(plugin.UrlDownload, filePath).ConfigureAwait(false);
+
+ Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
+ Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
+ }
+ catch (Exception e)
+ {
+ Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
+ Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
+
+ Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload");
+ }
+
+ Install(plugin, filePath);
+ Context.API.RestartApp();
+ }
+
+ internal List RequestUpdate(string search)
+ {
+ var autocompletedResults = AutoCompleteReturnAllResults(search,
+ Settings.HotkeyUpdate,
+ "Update",
+ "Select a plugin to update");
+
+ if (autocompletedResults.Any())
+ return autocompletedResults;
+
+ var uninstallSearch = search.Replace(Settings.HotkeyUpdate, string.Empty).TrimStart();
+
+
+ var resultsForUpdate =
+ from existingPlugin in Context.API.GetAllPlugins()
+ join pluginFromManifest in pluginsManifest.UserPlugins
+ on existingPlugin.Metadata.ID equals pluginFromManifest.ID
+ where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) < 0 // if current version precedes manifest version
+ select
+ new
+ {
+ pluginFromManifest.Name,
+ pluginFromManifest.Author,
+ CurrentVersion = existingPlugin.Metadata.Version,
+ NewVersion = pluginFromManifest.Version,
+ existingPlugin.Metadata.IcoPath,
+ PluginExistingMetadata = existingPlugin.Metadata,
+ PluginNewUserPlugin = pluginFromManifest
+ };
+
+ if (!resultsForUpdate.Any())
+ return new List
+ {
+ new Result
+ {
+ Title = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_title"),
+ SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_subtitle"),
+ IcoPath = icoPath
+ }
+ };
+
+
+ var results = resultsForUpdate
+ .Select(x =>
+ new Result
+ {
+ Title = $"{x.Name} by {x.Author}",
+ SubTitle = $"Update from version {x.CurrentVersion} to {x.NewVersion}",
+ IcoPath = x.IcoPath,
+ Action = e =>
+ {
+ string message = string.Format(
+ Context.API.GetTranslation("plugin_pluginsmanager_update_prompt"),
+ x.Name, x.Author,
+ Environment.NewLine, Environment.NewLine);
+
+ if (MessageBox.Show(message,
+ Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
+ MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ {
+ Uninstall(x.PluginExistingMetadata);
+
+ var downloadToFilePath = Path.Combine(DataLocation.PluginsDirectory,
+ $"{x.Name}-{x.NewVersion}.zip");
+
+ Task.Run(async delegate
+ {
+ await Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false);
+ Install(x.PluginNewUserPlugin, downloadToFilePath);
+
+ Context.API.RestartApp();
+ });
+
+ return true;
+ }
+
+ return false;
+ }
+ });
+
+ return Search(results, uninstallSearch);
+ }
+
+ internal bool PluginExists(string id)
+ {
+ return Context.API.GetAllPlugins().Any(x => x.Metadata.ID == id);
+ }
+
+ internal List Search(IEnumerable results, string searchName)
+ {
+ if (string.IsNullOrEmpty(searchName))
+ return results.ToList();
+
+ return results
+ .Where(x =>
+ {
+ var matchResult = StringMatcher.FuzzySearch(searchName, x.Title);
+ if (matchResult.IsSearchPrecisionScoreMet())
+ x.Score = matchResult.Score;
+
+ return matchResult.IsSearchPrecisionScoreMet();
+ })
+ .ToList();
+ }
+
+ internal List RequestInstallOrUpdate(string searchName)
+ {
+ var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim();
+
+ var results =
+ pluginsManifest
+ .UserPlugins
+ .Select(x =>
+ new Result
+ {
+ Title = $"{x.Name} by {x.Author}",
+ SubTitle = x.Description,
+ IcoPath = icoPath,
+ Action = e =>
+ {
+ Application.Current.MainWindow.Hide();
+ _ = InstallOrUpdate(x); // No need to wait
+ return ShouldHideWindow;
+ },
+ ContextData = x
+ });
+
+ return Search(results, searchNameWithoutKeyword);
+ }
+
+ private void Install(UserPlugin plugin, string downloadedFilePath)
+ {
+ if (!File.Exists(downloadedFilePath))
+ return;
+
+ var tempFolderPath = Path.Combine(Path.GetTempPath(), "flowlauncher");
+ var tempFolderPluginPath = Path.Combine(tempFolderPath, "plugin");
+
+ if (Directory.Exists(tempFolderPath))
+ Directory.Delete(tempFolderPath, true);
+
+ Directory.CreateDirectory(tempFolderPath);
+
+ var zipFilePath = Path.Combine(tempFolderPath, Path.GetFileName(downloadedFilePath));
+
+ File.Move(downloadedFilePath, zipFilePath);
+
+ Utilities.UnZip(zipFilePath, tempFolderPluginPath, true);
+
+ var pluginFolderPath = Utilities.GetContainingFolderPathAfterUnzip(tempFolderPluginPath);
+
+ var metadataJsonFilePath = string.Empty;
+ if (File.Exists(Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName)))
+ metadataJsonFilePath = Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName);
+
+ if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath))
+ {
+ MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_install_errormetadatafile"));
+ return;
+ }
+
+ string newPluginPath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}");
+
+ Directory.Move(pluginFolderPath, newPluginPath);
+ }
+
+ internal List RequestUninstall(string search)
+ {
+ var autocompletedResults = AutoCompleteReturnAllResults(search,
+ Settings.HotkeyUninstall,
+ "Uninstall",
+ "Select a plugin to uninstall");
+
+ if (autocompletedResults.Any())
+ return autocompletedResults;
+
+ var uninstallSearch = search.Replace(Settings.HotkeyUninstall, string.Empty).TrimStart();
+
+ var results = Context.API
+ .GetAllPlugins()
+ .Select(x =>
+ new Result
+ {
+ Title = $"{x.Metadata.Name} by {x.Metadata.Author}",
+ SubTitle = x.Metadata.Description,
+ IcoPath = x.Metadata.IcoPath,
+ Action = e =>
+ {
+ string message = string.Format(
+ Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt"),
+ x.Metadata.Name, x.Metadata.Author,
+ Environment.NewLine, Environment.NewLine);
+
+ if (MessageBox.Show(message,
+ Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
+ MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ {
+ Application.Current.MainWindow.Hide();
+ Uninstall(x.Metadata);
+ Context.API.RestartApp();
+
+ return true;
+ }
+
+ return false;
+ }
+ });
+
+ return Search(results, uninstallSearch);
+ }
+
+ private void Uninstall(PluginMetadata plugin)
+ {
+ // Marked for deletion. Will be deleted on next start up
+ using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
+ }
+
+ private List AutoCompleteReturnAllResults(string search, string hotkey, string title, string subtitle)
+ {
+ if (!string.IsNullOrEmpty(search)
+ && hotkey.StartsWith(search)
+ && (hotkey != search || !search.StartsWith(hotkey)))
+ {
+ return
+ new List
+ {
+ new Result
+ {
+ Title = title,
+ IcoPath = icoPath,
+ SubTitle = subtitle,
+ Action = e =>
+ {
+ Context
+ .API
+ .ChangeQuery(
+ $"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {hotkey} ");
+
+ return false;
+ }
+ }
+ };
+ }
+
+ return new List();
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs
new file mode 100644
index 00000000000..9c5b0d29f6b
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Flow.Launcher.Plugin.PluginsManager
+{
+ internal class Settings
+ {
+ internal string HotKeyInstall { get; set; } = "install";
+ internal string HotkeyUninstall { get; set; } = "uninstall";
+
+ internal string HotkeyUpdate { get; set; } = "update";
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs
new file mode 100644
index 00000000000..2853ffc9e28
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs
@@ -0,0 +1,61 @@
+using Flow.Launcher.Infrastructure.Http;
+using ICSharpCode.SharpZipLib.Zip;
+using System.IO;
+using System.Net;
+
+namespace Flow.Launcher.Plugin.PluginsManager
+{
+ internal static class Utilities
+ {
+ ///
+ /// Unzip contents to the given directory.
+ ///
+ /// The path to the zip file.
+ /// The output directory.
+ /// overwrite
+ internal static void UnZip(string zipFilePath, string strDirectory, bool overwrite)
+ {
+ if (strDirectory == "")
+ strDirectory = Directory.GetCurrentDirectory();
+
+ using var zipStream = new ZipInputStream(File.OpenRead(zipFilePath));
+
+ ZipEntry theEntry;
+
+ while ((theEntry = zipStream.GetNextEntry()) != null)
+ {
+ 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);
+
+ Directory.CreateDirectory(destinationDir);
+
+ if (string.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overwrite))
+ continue;
+
+ using var streamWriter = File.Create(destinationFile);
+ zipStream.CopyTo(streamWriter);
+ }
+ }
+
+ internal static string GetContainingFolderPathAfterUnzip(string unzippedParentFolderPath)
+ {
+ var unzippedFolderCount = Directory.GetDirectories(unzippedParentFolderPath).Length;
+ var unzippedFilesCount = Directory.GetFiles(unzippedParentFolderPath).Length;
+
+ // adjust path depending on how the plugin is zipped up
+ // the recommended should be to zip up the folder not the contents
+ if (unzippedFolderCount == 1 && unzippedFilesCount == 0)
+ // folder is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/pluginFolderName/
+ return Directory.GetDirectories(unzippedParentFolderPath)[0];
+
+ if (unzippedFilesCount > 1)
+ // content is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/
+ return unzippedParentFolderPath;
+
+ return string.Empty;
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ViewModels/SettingsViewModel.cs
new file mode 100644
index 00000000000..f3cf117d33f
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/ViewModels/SettingsViewModel.cs
@@ -0,0 +1,26 @@
+using Flow.Launcher.Infrastructure.Storage;
+using Flow.Launcher.Infrastructure.UserSettings;
+
+namespace Flow.Launcher.Plugin.PluginsManager.ViewModels
+{
+ public class SettingsViewModel
+ {
+ private readonly PluginJsonStorage storage;
+
+ internal Settings Settings { get; set; }
+
+ internal PluginInitContext Context { get; set; }
+
+ public SettingsViewModel(PluginInitContext context)
+ {
+ Context = context;
+ storage = new PluginJsonStorage();
+ Settings = storage.Load();
+ }
+
+ public void Save()
+ {
+ storage.Save();
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml
new file mode 100644
index 00000000000..89d27f6ffea
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml.cs
new file mode 100644
index 00000000000..14204eda930
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml.cs
@@ -0,0 +1,22 @@
+
+using Flow.Launcher.Plugin.PluginsManager.ViewModels;
+
+namespace Flow.Launcher.Plugin.PluginsManager.Views
+{
+ ///
+ /// Interaction logic for PluginsManagerSettings.xaml
+ ///
+ public partial class PluginsManagerSettings
+ {
+ private readonly SettingsViewModel viewModel;
+
+ public PluginsManagerSettings(SettingsViewModel viewModel)
+ {
+ InitializeComponent();
+
+ this.viewModel = viewModel;
+
+ //RefreshView();
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
new file mode 100644
index 00000000000..d94af71a129
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
@@ -0,0 +1,14 @@
+{
+ "ID": "9f8f9b14-2518-4907-b211-35ab6290dee7",
+ "ActionKeywords": [
+ "pm"
+ ],
+ "Name": "Plugins Manager",
+ "Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
+ "Author": "Jeremy Wu",
+ "Version": "1.3.1",
+ "Language": "csharp",
+ "Website": "https://github.com/Flow-Launcher/Flow.Launcher",
+ "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",
+ "IcoPath": "Images\\pluginsmanager.png"
+}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml
index ece9fea819e..851233407c1 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml
@@ -40,7 +40,12 @@
Vyhľadávanie programov vo Flow Launcheri
Neplatná cesta
-
+
+ Vlastný správca súborov
+ Arg.
+ Môžete si prispôsobiť otváranie umiestnenia priečinka vložením Premenných prostredia, ktoré chcete použiť. Dostupnosť premenných prostredia môžete vyskúšať cez príkazový riadok.
+ Zadajte argumenty, ktoré chcete pridať pre správcu súborov. %s pre rodičovský priečinok, %f pre celú cestu (funguje iba pre win32). Pre podrobnosti pozrite webovú stránku správcu súborov.
+
Úspešné
Úspešne zakázané zobrazovanie tohto programu vo výsledkoch vyhľadávania
diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json
index 9b5af94fbee..7d7a42e03ca 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json
@@ -4,7 +4,7 @@
"Name": "Program",
"Description": "Search programs in Flow.Launcher",
"Author": "qianlifeng",
- "Version": "1.2.1",
+ "Version": "1.2.2",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Program.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs
index 555ee4647e1..1a3d9e5e5d9 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs
@@ -196,7 +196,8 @@ public Settings()
[JsonIgnore]
public SuggestionSource[] Suggestions { get; set; } = {
new Google(),
- new Baidu()
+ new Baidu(),
+ new Bing()
};
[JsonIgnore]
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs
index 57db223bcb9..6772acf8256 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs
@@ -8,6 +8,7 @@
using Newtonsoft.Json.Linq;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
+using System.Net.Http;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
@@ -22,9 +23,9 @@ public override async Task> Suggestions(string query)
try
{
const string api = "http://suggestion.baidu.com/su?json=1&wd=";
- result = await Http.Get(api + Uri.EscapeUriString(query), "GB2312");
+ result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false);
}
- catch (WebException e)
+ catch (HttpRequestException e)
{
Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e);
return new List();
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs
new file mode 100644
index 00000000000..9c4746711e5
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs
@@ -0,0 +1,62 @@
+using Flow.Launcher.Infrastructure.Http;
+using Flow.Launcher.Infrastructure.Logger;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Text.Json;
+using System.Linq;
+
+namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
+{
+ class Bing : SuggestionSource
+ {
+ public override async Task> Suggestions(string query)
+ {
+ Stream resultStream;
+
+ try
+ {
+ const string api = "https://api.bing.com/qsonhs.aspx?q=";
+ resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false);
+ }
+ catch (HttpRequestException e)
+ {
+ Log.Exception("|Bing.Suggestions|Can't get suggestion from Bing", e);
+ return new List();
+ }
+
+ if (resultStream.Length == 0) return new List();
+
+ JsonElement json;
+ try
+ {
+ json = (await JsonDocument.ParseAsync(resultStream)).RootElement.GetProperty("AS");
+ }
+ catch (JsonException e)
+ {
+ Log.Exception("|Bing.Suggestions|can't parse suggestions", e);
+ return new List();
+ }
+
+ if (json.GetProperty("FullResults").GetInt32() == 0)
+ return new List();
+
+ return json.GetProperty("Results")
+ .EnumerateArray()
+ .SelectMany(r => r.GetProperty("Suggests")
+ .EnumerateArray()
+ .Select(s => s.GetProperty("Txt").GetString()))
+ .ToList();
+
+ }
+
+ public override string ToString()
+ {
+ return "Bing";
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs
index 81878bd8b4a..5b9538091b9 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs
@@ -7,6 +7,7 @@
using Newtonsoft.Json.Linq;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
+using System.Net.Http;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
@@ -18,13 +19,12 @@ public override async Task> Suggestions(string query)
try
{
const string api = "https://www.google.com/complete/search?output=chrome&q=";
- result = await Http.Get(api + Uri.EscapeUriString(query));
+ result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false);
}
- catch (WebException e)
+ catch (HttpRequestException e)
{
Log.Exception("|Google.Suggestions|Can't get suggestion from google", e);
return new List();
- ;
}
if (string.IsNullOrEmpty(result)) return new List();
JContainer json;
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json
index 329f1c41d92..99fd2210aac 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json
@@ -25,7 +25,7 @@
"Name": "Web Searches",
"Description": "Provide the web search ability",
"Author": "qianlifeng",
- "Version": "1.1.2",
+ "Version": "1.2.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll",
diff --git a/README.md b/README.md
index d7f8dd7ba9f..02f48875883 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
-
+
+
+

@@ -17,7 +19,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be

- Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse.
-- Search for file contents
+- Search for file contents.
- Support search using environment variable paths
- Run batch and PowerShell commands as Administrator or a different user.
- Support languages from Chinese to Italian and more.
@@ -26,20 +28,24 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
## Running Flow Launcher
-| [Windows 7 and up](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest)
-| ------------- |
+| [Windows 7 and up](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) |
+| ---------------------------------------------------------------------------------- |
Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up.
-**Integrations:**
- - If you use python plugins, install [python3](https://www.python.org/downloads/): `.exe` installer + add it to `%PATH%` or set it in flow's settings
- - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest)
+**Integrations**
+ - If you use Python plugins:
+ - Install [Python3](https://www.python.org/downloads/), download `.exe` installer.
+ - Add Python to `%PATH%` or set it in flow's settings.
+ - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`.
+ - Start to launch your Python plugins.
+ - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest).
**Usage**
-- Open flow's search window: Alt+Space is the default hotkey
-- Open context menu: Ctrl+O/Shift+Enter
-- Cancel/Return to previous screen: Esc
-- Install/Uninstall plugins: in the search window, type `wpm install/uninstall` + the plugin name
+- Open flow's search window: Alt+Space is the default hotkey.
+- Open context menu: Ctrl+O/Shift+Enter.
+- Cancel/Return to previous screen: Esc.
+- Install/Uninstall/Update plugins: in the search window, type `pm install`/`pm uninstall`/`pm update` + the plugin name.
- Saved user settings are located:
- If using roaming: `%APPDATA%\FlowLauncher`
- If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData`
@@ -74,10 +80,3 @@ Install .Net Core 3.1 SDK via Visual Studio installer or manually from [here](ht
## Documentation
[Wiki](https://github.com/Flow-Launcher/Flow.Launcher/wiki)
-
-## A history of the flow
-Flow's roots came from a rebrand of the [JJW24/Wox fork](https://github.com/jjw24/Wox/issues/156) and WoX.
-
-A big thank you and all credits to [Bao](https://github.com/bao-qian), the author of WoX, and its contrbutors for all the amazing work.
-
-The JJW24/Wox fork started adding new changes on top of main WoX repo's code base from release v1.3.524. Flow is a continuation of the work from JJW24/Wox
diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1
index 18ce33c4f07..59036842af4 100644
--- a/Scripts/post_build.ps1
+++ b/Scripts/post_build.ps1
@@ -36,7 +36,6 @@ function Copy-Resources ($path, $config) {
$output = "$path\Output"
$target = "$output\$config"
Copy-Item -Recurse -Force $project\Images\* $target\Images\
- Copy-Item -Recurse -Force $path\JsonRPC $target\JsonRPC
# making version static as multiple versions can exist in the nuget folder and in the case a breaking change is introduced.
Copy-Item -Force $env:USERPROFILE\.nuget\packages\squirrel.windows\1.5.2\tools\Squirrel.exe $output\Update.exe
}
diff --git a/SolutionAssemblyInfo.cs b/SolutionAssemblyInfo.cs
index 018084a668d..ccbfef5d031 100644
--- a/SolutionAssemblyInfo.cs
+++ b/SolutionAssemblyInfo.cs
@@ -16,6 +16,6 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
-[assembly: AssemblyVersion("1.5.0")]
-[assembly: AssemblyFileVersion("1.5.0")]
-[assembly: AssemblyInformationalVersion("1.5.0")]
\ No newline at end of file
+[assembly: AssemblyVersion("1.6.0")]
+[assembly: AssemblyFileVersion("1.6.0")]
+[assembly: AssemblyInformationalVersion("1.6.0")]
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index f5841da3ec0..1f0937d6ddb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: '1.5.0.{build}'
+version: '1.6.0.{build}'
init:
- ps: |
diff --git a/attribution.md b/attribution.md
new file mode 100644
index 00000000000..c47542d6aa8
--- /dev/null
+++ b/attribution.md
@@ -0,0 +1,6 @@
+Flow's roots came from a rebrand of the [JJW24/Wox fork](https://github.com/jjw24/Wox/issues/156) and [WoX](https://github.com/Wox-launcher/Wox).
+
+A big thank you and all credits to [Bao](https://github.com/bao-qian), the author of WoX, and its contributors for all the amazing work.
+
+The JJW24/Wox fork started adding new changes on top of main WoX repo's code base from release v1.3.524.
+Flow is a continuation of the work from JJW24/Wox.