using System; using System.IO.Pipes; using System.IO; using System.Security.Principal; using System.Threading.Tasks; namespace Flow.Launcher.Plugin.QuickLook.Helpers { /// /// Adapted from Files /// https://github.com/files-community/Files/blob/ad33c75c53382fcb9b16fa9cd66ae5399f3dff0b/src/Files.App/Helpers/QuickLookHelpers.cs /// internal static class QuickLookHelper { private static readonly IPublicAPI api = Main.Context.API; private const int TIMEOUT = 500; private static DateTime lastNotificationTime = DateTime.MinValue; private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; /// /// Toggle QuickLook /// /// File path to preview /// Send toast when fails. /// public static async Task ToggleQuickLookAsync(string path, bool sendFailToast = true) { if (string.IsNullOrEmpty(path)) return false; bool success = await SendQuickLookPipeMsgAsync(pipeMessageToggle, path); if (sendFailToast && !success) { ShowQuickLookUnavailableToast(); } return success; } public static async Task CloseQuickLookAsync() { bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); return success; } public static async Task OpenQuickLookAsync(string path, bool sendFailToast = true) { if (string.IsNullOrEmpty(path)) return false; bool success = await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); if (sendFailToast && !success) { ShowQuickLookUnavailableToast(); } return success; } /// /// Switch QuickLook to preview another file if it's on /// /// File path to preview /// Send notification if fail /// public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = true) { if (string.IsNullOrEmpty(path)) return false; bool success = await SendQuickLookPipeMsgAsync(pipeMessageSwitch, path); if (sendFailToast && !success) { ShowQuickLookUnavailableToast(); } return success; } private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") { await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); try { await client.ConnectAsync(TIMEOUT); await using var writer = new StreamWriter(client); await writer.WriteLineAsync($"{message}|{arg}"); await writer.FlushAsync(); } catch (TimeoutException e) { client.Close(); api.LogException($"{nameof(QuickLookHelper)}", "QuickLook timeout", e); return false; } catch (Exception e) { api.LogException($"{nameof(QuickLookHelper)}", "QuickLook error", e); return false; } return true; } public static async Task DetectQuickLookAvailabilityAsync() { static async Task QuickLookServerAvailable() { await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); try { await client.ConnectAsync(TIMEOUT); var serverInstances = client.NumberOfServerInstances; await using var writer = new StreamWriter(client); await writer.WriteLineAsync($"{pipeMessageSwitch}|"); await writer.FlushAsync(); return serverInstances; } catch (TimeoutException e) { client.Close(); api.LogException($"{nameof(QuickLookHelper)}", "QuickLook connection timeout", e); return 0; } } try { var result = await QuickLookServerAvailable(); return result != 0; } catch (Exception e) { api.LogException($"{nameof(QuickLookHelper)}", "QuickLook unavailable", e); return false; } } private static void ShowQuickLookUnavailableToast() { if (lastNotificationTime.AddSeconds(10) < DateTime.Now) { api.ShowMsgError(api.GetTranslation("quicklook_failed_to_launch"), api.GetTranslation("quicklook_fail_tips")); lastNotificationTime = DateTime.Now; } } } }