using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using System.Windows; using CommunityToolkit.Mvvm.DependencyInjection; using Squirrel; using Flow.Launcher.Core; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.ViewModel; using JetBrains.Annotations; using Flow.Launcher.Core.Resource; namespace Flow.Launcher { public class PublicAPIInstance : IPublicAPI { private readonly Settings _settings; private readonly Internationalization _translater; private readonly MainViewModel _mainVM; #region Constructor public PublicAPIInstance(Settings settings, Internationalization translater, MainViewModel mainVM) { _settings = settings; _translater = translater; _mainVM = mainVM; GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback; WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); } #endregion #region Public API public void ChangeQuery(string query, bool requery = false) { _mainVM.ChangeQueryText(query, requery); } public void RestartApp() { _mainVM.Hide(); // we must manually save // UpdateManager.RestartApp() will call Environment.Exit(0) // which will cause ungraceful exit SaveAppAllSettings(); // Restart requires Squirrel's Update.exe to be present in the parent folder, // it is only published from the project's release pipeline. When debugging without it, // the project may not restart or just terminates. This is expected. UpdateManager.RestartApp(Constant.ApplicationFileName); } public void ShowMainWindow() => _mainVM.Show(); public void HideMainWindow() => _mainVM.Hide(); public bool IsMainWindowVisible() => _mainVM.MainWindowVisibilityStatus; public event VisibilityChangedEventHandler VisibilityChanged { add => _mainVM.VisibilityChanged += value; remove => _mainVM.VisibilityChanged -= value; } // Must use Ioc.Default.GetRequiredService() to avoid circular dependency public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService().UpdateAppAsync(false); public void SaveAppAllSettings() { PluginManager.Save(); _mainVM.Save(); _settings.Save(); _ = ImageLoader.Save(); } public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync(); public void ShowMsgError(string title, string subTitle = "") => ShowMsg(title, subTitle, Constant.ErrorIcon, true); public void ShowMsg(string title, string subTitle = "", string iconPath = "") => ShowMsg(title, subTitle, iconPath, true); public void ShowMsg(string title, string subTitle, string iconPath, bool useMainWindowAsOwner = true) { Notification.Show(title, subTitle, iconPath); } public void OpenSettingDialog() { Application.Current.Dispatcher.Invoke(() => { SettingWindow sw = SingletonWindowOpener.Open(); }); } public void ShellRun(string cmd, string filename = "cmd.exe") { var args = filename == "cmd.exe" ? $"/C {cmd}" : $"{cmd}"; var startInfo = ShellCommand.SetProcessStartInfo(filename, arguments: args, createNoWindow: true); ShellCommand.Execute(startInfo); } public void CopyToClipboard(string stringToCopy, bool directCopy = false, bool showDefaultNotification = true) { if (string.IsNullOrEmpty(stringToCopy)) return; var isFile = File.Exists(stringToCopy); if (directCopy && (isFile || Directory.Exists(stringToCopy))) { var paths = new StringCollection { stringToCopy }; Clipboard.SetFileDropList(paths); if (showDefaultNotification) ShowMsg( $"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}", GetTranslation("completedSuccessfully")); } else { Clipboard.SetDataObject(stringToCopy); if (showDefaultNotification) ShowMsg( $"{GetTranslation("copy")} {GetTranslation("textTitle")}", GetTranslation("completedSuccessfully")); } } public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible; public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed; public string GetTranslation(string key) => _translater.GetTranslation(key); public List GetAllPlugins() => PluginManager.AllPlugins.ToList(); public MatchResult FuzzySearch(string query, string stringToCompare) => StringMatcher.FuzzySearch(query, stringToCompare); public Task HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url, token); public Task HttpGetStreamAsync(string url, CancellationToken token = default) => Http.GetStreamAsync(url, token); public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action reportProgress = null, CancellationToken token = default) => Http.DownloadAsync(url, filePath, reportProgress, token); public void AddActionKeyword(string pluginId, string newActionKeyword) => PluginManager.AddActionKeyword(pluginId, newActionKeyword); public bool ActionKeywordAssigned(string actionKeyword) => PluginManager.ActionKeywordRegistered(actionKeyword); public void RemoveActionKeyword(string pluginId, string oldActionKeyword) => PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") => Log.Debug(className, message, methodName); public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") => Log.Info(className, message, methodName); public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") => Log.Warn(className, message, methodName); public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName); private readonly ConcurrentDictionary _pluginJsonStorages = new(); public object RemovePluginSettings(string assemblyName) { foreach (var keyValuePair in _pluginJsonStorages) { var key = keyValuePair.Key; var value = keyValuePair.Value; var name = value.GetType().GetField("AssemblyName")?.GetValue(value)?.ToString(); if (name == assemblyName) { _pluginJsonStorages.Remove(key, out var pluginJsonStorage); return pluginJsonStorage; } } return null; } /// /// Save plugin settings. /// public void SavePluginSettings() { foreach (var value in _pluginJsonStorages.Values) { var method = value.GetType().GetMethod("Save"); method?.Invoke(value, null); } } public T LoadSettingJsonStorage() where T : new() { var type = typeof(T); if (!_pluginJsonStorages.ContainsKey(type)) _pluginJsonStorages[type] = new PluginJsonStorage(); return ((PluginJsonStorage)_pluginJsonStorages[type]).Load(); } public void SaveSettingJsonStorage() where T : new() { var type = typeof(T); if (!_pluginJsonStorages.ContainsKey(type)) _pluginJsonStorages[type] = new PluginJsonStorage(); ((PluginJsonStorage)_pluginJsonStorages[type]).Save(); } public void SaveJsonStorage(T settings) where T : new() { var type = typeof(T); _pluginJsonStorages[type] = new PluginJsonStorage(settings); ((PluginJsonStorage)_pluginJsonStorages[type]).Save(); } public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null) { using var explorer = new Process(); var explorerInfo = _settings.CustomExplorer; explorer.StartInfo = new ProcessStartInfo { FileName = explorerInfo.Path.Replace("%d", DirectoryPath), UseShellExecute = true, Arguments = FileNameOrFilePath is null ? explorerInfo.DirectoryArgument.Replace("%d", DirectoryPath) : explorerInfo.FileArgument .Replace("%d", DirectoryPath) .Replace("%f", Path.IsPathRooted(FileNameOrFilePath) ? FileNameOrFilePath : Path.Combine(DirectoryPath, FileNameOrFilePath) ) }; explorer.Start(); } private void OpenUri(Uri uri, bool? inPrivate = null) { if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) { var browserInfo = _settings.CustomBrowser; var path = browserInfo.Path == "*" ? "" : browserInfo.Path; if (browserInfo.OpenInTab) { uri.AbsoluteUri.OpenInBrowserTab(path, inPrivate ?? browserInfo.EnablePrivate, browserInfo.PrivateArg); } else { uri.AbsoluteUri.OpenInBrowserWindow(path, inPrivate ?? browserInfo.EnablePrivate, browserInfo.PrivateArg); } } else { Process.Start(new ProcessStartInfo() { FileName = uri.AbsoluteUri, UseShellExecute = true })?.Dispose(); return; } } public void OpenUrl(string url, bool? inPrivate = null) { OpenUri(new Uri(url), inPrivate); } public void OpenUrl(Uri url, bool? inPrivate = null) { OpenUri(url, inPrivate); } public void OpenAppUri(string appUri) { OpenUri(new Uri(appUri)); } public void OpenAppUri(Uri appUri) { OpenUri(appUri); } public void ToggleGameMode() { _mainVM.ToggleGameMode(); } public void SetGameMode(bool value) { _mainVM.GameModeStatus = value; } public bool IsGameModeOn() { return _mainVM.GameModeStatus; } private readonly List> _globalKeyboardHandlers = new(); public void RegisterGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Add(callback); public void RemoveGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Remove(callback); public void ReQuery(bool reselect = true) => _mainVM.ReQuery(reselect); public void BackToQueryResults() => _mainVM.BackToQueryResults(); public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) => MessageBoxEx.Show(messageBoxText, caption, button, icon, defaultResult); public Task ShowProgressBoxAsync(string caption, Func, Task> reportProgressAsync, Action cancelProgress = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, cancelProgress); #endregion #region Private Methods private bool KListener_hookedKeyboardCallback(KeyEvent keyevent, int vkcode, SpecialKeyState state) { var continueHook = true; foreach (var x in _globalKeyboardHandlers) { continueHook &= x((int)keyevent, vkcode, state); } return continueHook; } #endregion } }