Skip to content

Commit 762b267

Browse files
committed
Merge branch 'dev' into 250405-KoreanIME
2 parents 736837e + 75f23aa commit 762b267

File tree

29 files changed

+283
-161
lines changed

29 files changed

+283
-161
lines changed

Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Flow.Launcher.Infrastructure.Http;
22
using Flow.Launcher.Infrastructure.Logger;
3+
using Flow.Launcher.Plugin;
34
using System;
45
using System.Collections.Generic;
56
using System.Net;

Flow.Launcher.Core/ExternalPlugins/CommunityPluginStore.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using System.Threading;
44
using System.Threading.Tasks;
5+
using Flow.Launcher.Plugin;
56

67
namespace Flow.Launcher.Core.ExternalPlugins
78
{

Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
using Flow.Launcher.Infrastructure.Logger;
2-
using System;
1+
using System;
32
using System.Collections.Generic;
43
using System.Threading;
54
using System.Threading.Tasks;
5+
using CommunityToolkit.Mvvm.DependencyInjection;
6+
using Flow.Launcher.Plugin;
67

78
namespace Flow.Launcher.Core.ExternalPlugins
89
{
@@ -17,11 +18,11 @@ public static class PluginsManifest
1718
private static readonly SemaphoreSlim manifestUpdateLock = new(1);
1819

1920
private static DateTime lastFetchedAt = DateTime.MinValue;
20-
private static TimeSpan fetchTimeout = TimeSpan.FromMinutes(2);
21+
private static readonly TimeSpan fetchTimeout = TimeSpan.FromMinutes(2);
2122

2223
public static List<UserPlugin> UserPlugins { get; private set; }
2324

24-
public static async Task<bool> UpdateManifestAsync(CancellationToken token = default, bool usePrimaryUrlOnly = false)
25+
public static async Task<bool> UpdateManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default)
2526
{
2627
try
2728
{
@@ -43,7 +44,7 @@ public static async Task<bool> UpdateManifestAsync(CancellationToken token = def
4344
}
4445
catch (Exception e)
4546
{
46-
Log.Exception($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Http request failed", e);
47+
Ioc.Default.GetRequiredService<IPublicAPI>().LogException(nameof(PluginsManifest), "Http request failed", e);
4748
}
4849
finally
4950
{

Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -454,34 +454,23 @@ private static bool SameOrLesserPluginVersionExists(string metadataPath)
454454

455455
#region Public functions
456456

457-
public static bool PluginModified(string uuid)
457+
public static bool PluginModified(string id)
458458
{
459-
return _modifiedPlugins.Contains(uuid);
459+
return _modifiedPlugins.Contains(id);
460460
}
461461

462-
463-
/// <summary>
464-
/// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url,
465-
/// unless it's a local path installation
466-
/// </summary>
467462
public static async Task UpdatePluginAsync(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath)
468463
{
469464
InstallPlugin(newVersion, zipFilePath, checkModified:false);
470465
await UninstallPluginAsync(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false);
471466
_modifiedPlugins.Add(existingVersion.ID);
472467
}
473468

474-
/// <summary>
475-
/// Install a plugin. By default will remove the zip file if installation is from url, unless it's a local path installation
476-
/// </summary>
477469
public static void InstallPlugin(UserPlugin plugin, string zipFilePath)
478470
{
479471
InstallPlugin(plugin, zipFilePath, checkModified: true);
480472
}
481473

482-
/// <summary>
483-
/// Uninstall a plugin.
484-
/// </summary>
485474
public static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false)
486475
{
487476
await UninstallPluginAsync(plugin, removePluginFromSettings, removePluginSettings, true);
@@ -525,20 +514,20 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c
525514
var folderName = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}";
526515

527516
var defaultPluginIDs = new List<string>
528-
{
529-
"0ECADE17459B49F587BF81DC3A125110", // BrowserBookmark
530-
"CEA0FDFC6D3B4085823D60DC76F28855", // Calculator
531-
"572be03c74c642baae319fc283e561a8", // Explorer
532-
"6A122269676E40EB86EB543B945932B9", // PluginIndicator
533-
"9f8f9b14-2518-4907-b211-35ab6290dee7", // PluginsManager
534-
"b64d0a79-329a-48b0-b53f-d658318a1bf6", // ProcessKiller
535-
"791FC278BA414111B8D1886DFE447410", // Program
536-
"D409510CD0D2481F853690A07E6DC426", // Shell
537-
"CEA08895D2544B019B2E9C5009600DF4", // Sys
538-
"0308FD86DE0A4DEE8D62B9B535370992", // URL
539-
"565B73353DBF4806919830B9202EE3BF", // WebSearch
540-
"5043CETYU6A748679OPA02D27D99677A" // WindowsSettings
541-
};
517+
{
518+
"0ECADE17459B49F587BF81DC3A125110", // BrowserBookmark
519+
"CEA0FDFC6D3B4085823D60DC76F28855", // Calculator
520+
"572be03c74c642baae319fc283e561a8", // Explorer
521+
"6A122269676E40EB86EB543B945932B9", // PluginIndicator
522+
"9f8f9b14-2518-4907-b211-35ab6290dee7", // PluginsManager
523+
"b64d0a79-329a-48b0-b53f-d658318a1bf6", // ProcessKiller
524+
"791FC278BA414111B8D1886DFE447410", // Program
525+
"D409510CD0D2481F853690A07E6DC426", // Shell
526+
"CEA08895D2544B019B2E9C5009600DF4", // Sys
527+
"0308FD86DE0A4DEE8D62B9B535370992", // URL
528+
"565B73353DBF4806919830B9202EE3BF", // WebSearch
529+
"5043CETYU6A748679OPA02D27D99677A" // WindowsSettings
530+
};
542531

543532
// Treat default plugin differently, it needs to be removable along with each flow release
544533
var installDirectory = !defaultPluginIDs.Any(x => x == plugin.ID)

Flow.Launcher.Infrastructure/Image/ImageLoader.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55
using System.Linq;
66
using System.Threading;
77
using System.Threading.Tasks;
8-
using System.Windows.Documents;
98
using System.Windows.Media;
109
using System.Windows.Media.Imaging;
1110
using Flow.Launcher.Infrastructure.Logger;
1211
using Flow.Launcher.Infrastructure.Storage;
13-
using static Flow.Launcher.Infrastructure.Http.Http;
1412

1513
namespace Flow.Launcher.Infrastructure.Image
1614
{
@@ -28,7 +26,6 @@ public static class ImageLoader
2826
public const int SmallIconSize = 64;
2927
public const int FullIconSize = 256;
3028

31-
3229
private static readonly string[] ImageExtensions = { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico" };
3330

3431
public static async Task InitializeAsync()
@@ -183,7 +180,7 @@ private static async ValueTask<ImageResult> LoadInternalAsync(string path, bool
183180
private static async Task<BitmapImage> LoadRemoteImageAsync(bool loadFullImage, Uri uriResult)
184181
{
185182
// Download image from url
186-
await using var resp = await GetStreamAsync(uriResult);
183+
await using var resp = await Http.Http.GetStreamAsync(uriResult);
187184
await using var buffer = new MemoryStream();
188185
await resp.CopyToAsync(buffer);
189186
buffer.Seek(0, SeekOrigin.Begin);
@@ -287,7 +284,7 @@ public static bool TryGetValue(string path, bool loadFullImage, out ImageSource
287284
return ImageCache.TryGetValue(path, loadFullImage, out image);
288285
}
289286

290-
public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullImage = false)
287+
public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullImage = false, bool cacheImage = true)
291288
{
292289
var imageResult = await LoadInternalAsync(path, loadFullImage);
293290

@@ -303,16 +300,18 @@ public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullI
303300
// image already exists
304301
img = ImageCache[key, loadFullImage] ?? img;
305302
}
306-
else
303+
else if (cacheImage)
307304
{
308-
// new guid
309-
305+
// save guid key
310306
GuidToKey[hash] = path;
311307
}
312308
}
313309

314-
// update cache
315-
ImageCache[path, loadFullImage] = img;
310+
if (cacheImage)
311+
{
312+
// update cache
313+
ImageCache[path, loadFullImage] = img;
314+
}
316315
}
317316

318317
return img;

Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Threading;
99
using System.Threading.Tasks;
1010
using System.Windows;
11+
using System.Windows.Media;
1112

1213
namespace Flow.Launcher.Plugin
1314
{
@@ -344,5 +345,76 @@ public interface IPublicAPI
344345
/// Stop the loading bar in main window
345346
/// </summary>
346347
public void StopLoadingBar();
348+
349+
/// <summary>
350+
/// Load image from path. Support local, remote and data:image url.
351+
/// If image path is missing, it will return a missing icon.
352+
/// </summary>
353+
/// <param name="path">The path of the image.</param>
354+
/// <param name="loadFullImage">
355+
/// Load full image or not.
356+
/// </param>
357+
/// <param name="cacheImage">
358+
/// Cache the image or not. Cached image will be stored in FL cache.
359+
/// If the image is just used one time, it's better to set this to false.
360+
/// </param>
361+
/// <returns></returns>
362+
ValueTask<ImageSource> LoadImageAsync(string path, bool loadFullImage = false, bool cacheImage = true);
363+
364+
/// Update the plugin manifest
365+
/// </summary>
366+
/// <param name="usePrimaryUrlOnly">
367+
/// FL has multiple urls to download the plugin manifest. Set this to true to only use the primary url.
368+
/// </param>
369+
/// <param name="token"></param>
370+
/// <returns>True if the manifest is updated successfully, false otherwise</returns>
371+
public Task<bool> UpdatePluginManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default);
372+
373+
/// <summary>
374+
/// Get the plugin manifest
375+
/// </summary>
376+
/// <returns></returns>
377+
public IReadOnlyList<UserPlugin> GetPluginManifest();
378+
379+
/// <summary>
380+
/// Check if the plugin has been modified.
381+
/// If this plugin is updated, installed or uninstalled and users do not restart the app,
382+
/// it will be marked as modified
383+
/// </summary>
384+
/// <param name="id">Plugin id</param>
385+
/// <returns></returns>
386+
public bool PluginModified(string id);
387+
388+
/// <summary>
389+
/// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url,
390+
/// unless it's a local path installation
391+
/// </summary>
392+
/// <param name="pluginMetadata">The metadata of the old plugin to update</param>
393+
/// <param name="plugin">The new plugin to update</param>
394+
/// <param name="zipFilePath">
395+
/// Path to the zip file containing the plugin. It will be unzipped to the temporary directory, removed and installed.
396+
/// </param>
397+
/// <returns></returns>
398+
public Task UpdatePluginAsync(PluginMetadata pluginMetadata, UserPlugin plugin, string zipFilePath);
399+
400+
/// <summary>
401+
/// Install a plugin. By default will remove the zip file if installation is from url,
402+
/// unless it's a local path installation
403+
/// </summary>
404+
/// <param name="plugin">The plugin to install</param>
405+
/// <param name="zipFilePath">
406+
/// Path to the zip file containing the plugin. It will be unzipped to the temporary directory, removed and installed.
407+
/// </param>
408+
public void InstallPlugin(UserPlugin plugin, string zipFilePath);
409+
410+
/// <summary>
411+
/// Uninstall a plugin
412+
/// </summary>
413+
/// <param name="pluginMetadata">The metadata of the plugin to uninstall</param>
414+
/// <param name="removePluginSettings">
415+
/// Plugin has their own settings. If this is set to true, the plugin settings will be removed.
416+
/// </param>
417+
/// <returns></returns>
418+
public Task UninstallPluginAsync(PluginMetadata pluginMetadata, bool removePluginSettings = false);
347419
}
348420
}

Flow.Launcher.Plugin/UserPlugin.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using System;
2+
3+
namespace Flow.Launcher.Plugin
4+
{
5+
/// <summary>
6+
/// User Plugin Model for Flow Launcher
7+
/// </summary>
8+
public record UserPlugin
9+
{
10+
/// <summary>
11+
/// Unique identifier of the plugin
12+
/// </summary>
13+
public string ID { get; set; }
14+
15+
/// <summary>
16+
/// Name of the plugin
17+
/// </summary>
18+
public string Name { get; set; }
19+
20+
/// <summary>
21+
/// Description of the plugin
22+
/// </summary>
23+
public string Description { get; set; }
24+
25+
/// <summary>
26+
/// Author of the plugin
27+
/// </summary>
28+
public string Author { get; set; }
29+
30+
/// <summary>
31+
/// Version of the plugin
32+
/// </summary>
33+
public string Version { get; set; }
34+
35+
/// <summary>
36+
/// Allow language of the plugin <see cref="AllowedLanguage"/>
37+
/// </summary>
38+
public string Language { get; set; }
39+
40+
/// <summary>
41+
/// Website of the plugin
42+
/// </summary>
43+
public string Website { get; set; }
44+
45+
/// <summary>
46+
/// URL to download the plugin
47+
/// </summary>
48+
public string UrlDownload { get; set; }
49+
50+
/// <summary>
51+
/// URL to the source code of the plugin
52+
/// </summary>
53+
public string UrlSourceCode { get; set; }
54+
55+
/// <summary>
56+
/// Local path where the plugin is installed
57+
/// </summary>
58+
public string LocalInstallPath { get; set; }
59+
60+
/// <summary>
61+
/// Icon path of the plugin
62+
/// </summary>
63+
public string IcoPath { get; set; }
64+
65+
/// <summary>
66+
/// The date when the plugin was last updated
67+
/// </summary>
68+
public DateTime? LatestReleaseDate { get; set; }
69+
70+
/// <summary>
71+
/// The date when the plugin was added to the local system
72+
/// </summary>
73+
public DateTime? DateAdded { get; set; }
74+
75+
/// <summary>
76+
/// Indicates whether the plugin is installed from a local path
77+
/// </summary>
78+
public bool IsFromLocalInstallPath => !string.IsNullOrEmpty(LocalInstallPath);
79+
}
80+
}

Flow.Launcher/App.xaml.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,6 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
179179
Current.MainWindow = _mainWindow;
180180
Current.MainWindow.Title = Constant.FlowLauncher;
181181

182-
HotKeyMapper.Initialize();
183-
184182
// main windows needs initialized before theme change because of blur settings
185183
Ioc.Default.GetRequiredService<Theme>().ChangeTheme();
186184

0 commit comments

Comments
 (0)