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 @@

- + + +

![Maintenance](https://img.shields.io/maintenance/yes/2020) @@ -17,7 +19,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be ![The Flow](https://user-images.githubusercontent.com/26427004/82151677-fa9c7100-989f-11ea-9143-81de60aaf07d.gif) - 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.