mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'dev' into administrator_mode
This commit is contained in:
commit
2c8df64559
6 changed files with 98 additions and 30 deletions
10
.github/workflows/dotnet.yml
vendored
10
.github/workflows/dotnet.yml
vendored
|
|
@ -54,28 +54,28 @@ jobs:
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: .\Scripts\post_build.ps1
|
run: .\Scripts\post_build.ps1
|
||||||
- name: Upload Plugin Nupkg
|
- name: Upload Plugin Nupkg
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: Plugin nupkg
|
name: Plugin nupkg
|
||||||
path: |
|
path: |
|
||||||
Output\Release\Flow.Launcher.Plugin.*.nupkg
|
Output\Release\Flow.Launcher.Plugin.*.nupkg
|
||||||
compression-level: 0
|
compression-level: 0
|
||||||
- name: Upload Setup
|
- name: Upload Setup
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: Flow Installer
|
name: Flow Installer
|
||||||
path: |
|
path: |
|
||||||
Output\Packages\Flow-Launcher-*.exe
|
Output\Packages\Flow-Launcher-*.exe
|
||||||
compression-level: 0
|
compression-level: 0
|
||||||
- name: Upload Portable Version
|
- name: Upload Portable Version
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: Portable Version
|
name: Portable Version
|
||||||
path: |
|
path: |
|
||||||
Output\Packages\Flow-Launcher-Portable.zip
|
Output\Packages\Flow-Launcher-Portable.zip
|
||||||
compression-level: 0
|
compression-level: 0
|
||||||
- name: Upload Full Nupkg
|
- name: Upload Full Nupkg
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: Full nupkg
|
name: Full nupkg
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -83,7 +83,7 @@ jobs:
|
||||||
|
|
||||||
compression-level: 0
|
compression-level: 0
|
||||||
- name: Upload Release Information
|
- name: Upload Release Information
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: RELEASES
|
name: RELEASES
|
||||||
path: |
|
path: |
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,11 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
||||||
|
|
||||||
public static async Task<bool> UpdateManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default)
|
public static async Task<bool> UpdateManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
|
bool lockAcquired = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await manifestUpdateLock.WaitAsync(token).ConfigureAwait(false);
|
await manifestUpdateLock.WaitAsync(token).ConfigureAwait(false);
|
||||||
|
lockAcquired = true;
|
||||||
|
|
||||||
if (UserPlugins == null || usePrimaryUrlOnly || DateTime.Now.Subtract(lastFetchedAt) >= fetchTimeout)
|
if (UserPlugins == null || usePrimaryUrlOnly || DateTime.Now.Subtract(lastFetchedAt) >= fetchTimeout)
|
||||||
{
|
{
|
||||||
|
|
@ -54,13 +56,18 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
PublicApi.Instance.LogException(ClassName, "Http request failed", e);
|
PublicApi.Instance.LogException(ClassName, "Http request failed", e);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
manifestUpdateLock.Release();
|
// Only release the lock if it was acquired
|
||||||
|
if (lockAcquired) manifestUpdateLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -454,7 +454,7 @@
|
||||||
<system:String x:Key="icons">Icons</system:String>
|
<system:String x:Key="icons">Icons</system:String>
|
||||||
<system:String x:Key="about_activate_times">You have activated Flow Launcher {0} times</system:String>
|
<system:String x:Key="about_activate_times">You have activated Flow Launcher {0} times</system:String>
|
||||||
<system:String x:Key="checkUpdates">Check for Updates</system:String>
|
<system:String x:Key="checkUpdates">Check for Updates</system:String>
|
||||||
<system:String x:Key="BecomeASponsor">Become A Sponsor</system:String>
|
<system:String x:Key="BecomeASponsor">Become a Sponsor</system:String>
|
||||||
<system:String x:Key="newVersionTips">New version {0} is available, would you like to restart Flow Launcher to use the update?</system:String>
|
<system:String x:Key="newVersionTips">New version {0} is available, would you like to restart Flow Launcher to use the update?</system:String>
|
||||||
<system:String x:Key="checkUpdatesFailed">Check updates failed, please check your connection and proxy settings to api.github.com.</system:String>
|
<system:String x:Key="checkUpdatesFailed">Check updates failed, please check your connection and proxy settings to api.github.com.</system:String>
|
||||||
<system:String x:Key="downloadUpdatesFailed">
|
<system:String x:Key="downloadUpdatesFailed">
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,19 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async ValueTask<bool> IsEverythingRunningAsync(CancellationToken token = default)
|
public static async ValueTask<bool> IsEverythingRunningAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
await _semaphore.WaitAsync(token);
|
await _semaphore.WaitAsync(token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EverythingApiDllImport.Everything_GetMajorVersion();
|
_ = EverythingApiDllImport.Everything_GetMajorVersion();
|
||||||
var result = EverythingApiDllImport.Everything_GetLastError() != StateCode.IPCError;
|
var result = EverythingApiDllImport.Everything_GetLastError() != StateCode.IPCError;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -77,8 +84,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
||||||
if (option.MaxCount < 0)
|
if (option.MaxCount < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(option.MaxCount), option.MaxCount, "MaxCount must be greater than or equal to 0");
|
throw new ArgumentOutOfRangeException(nameof(option.MaxCount), option.MaxCount, "MaxCount must be greater than or equal to 0");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
await _semaphore.WaitAsync(token);
|
await _semaphore.WaitAsync(token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -120,8 +133,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
||||||
EverythingApiDllImport.Everything_SetRequestFlags(EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME);
|
EverythingApiDllImport.Everything_SetRequestFlags(EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (token.IsCancellationRequested) yield break;
|
if (token.IsCancellationRequested) yield break;
|
||||||
|
|
||||||
if (!EverythingApiDllImport.Everything_QueryW(true))
|
if (!EverythingApiDllImport.Everything_QueryW(true))
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(query.Search) && ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword))
|
if (string.IsNullOrEmpty(query.Search) && ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword))
|
||||||
return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks);
|
return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks);
|
||||||
|
|
||||||
var quickAccessLinks = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks);
|
|
||||||
|
|
||||||
results.UnionWith(quickAccessLinks);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// No action keyword matched- plugin should not handle this query, return empty results.
|
||||||
return new List<Result>();
|
return new List<Result>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,10 +100,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
||||||
searchResults = Settings.IndexProvider.SearchAsync(query.Search, token);
|
searchResults = Settings.IndexProvider.SearchAsync(query.Search, token);
|
||||||
engineName = Enum.GetName(Settings.IndexSearchEngine);
|
engineName = Enum.GetName(Settings.IndexSearchEngine);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case true or false
|
||||||
|
when ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword):
|
||||||
|
return QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return results.ToList();
|
return results.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge Quick Access Link results for non-path searches.
|
||||||
|
results.UnionWith(QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false))
|
await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false))
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ namespace Flow.Launcher.Plugin.Program
|
||||||
|
|
||||||
internal static PluginInitContext Context { get; private set; }
|
internal static PluginInitContext Context { get; private set; }
|
||||||
|
|
||||||
|
private static readonly Lock _lastIndexTimeLock = new();
|
||||||
|
|
||||||
private static readonly List<Result> emptyResults = [];
|
private static readonly List<Result> emptyResults = [];
|
||||||
|
|
||||||
private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 };
|
private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 };
|
||||||
|
|
@ -81,9 +83,46 @@ namespace Flow.Launcher.Plugin.Program
|
||||||
var result = await cache.GetOrCreateAsync(query.Search, async entry =>
|
var result = await cache.GetOrCreateAsync(query.Search, async entry =>
|
||||||
{
|
{
|
||||||
var resultList = await Task.Run(async () =>
|
var resultList = await Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// Preparing win32 programs
|
||||||
|
List<Win32> win32s;
|
||||||
|
bool win32LockAcquired = false;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
await _win32sLock.WaitAsync(token);
|
await _win32sLock.WaitAsync(token);
|
||||||
|
win32LockAcquired = true;
|
||||||
|
win32s = [.. _win32s];
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return emptyResults;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Only release the lock if it was acquired
|
||||||
|
if (win32LockAcquired) _win32sLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preparing UWP programs
|
||||||
|
List<UWPApp> uwps;
|
||||||
|
bool uwpsLockAcquired = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
await _uwpsLock.WaitAsync(token);
|
await _uwpsLock.WaitAsync(token);
|
||||||
|
uwpsLockAcquired = true;
|
||||||
|
uwps = [.. _uwps];
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return emptyResults;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Only release the lock if it was acquired
|
||||||
|
if (uwpsLockAcquired) _uwpsLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start querying programs
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Collect all UWP Windows app directories
|
// Collect all UWP Windows app directories
|
||||||
|
|
@ -94,8 +133,8 @@ namespace Flow.Launcher.Plugin.Program
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.ToArray() : null;
|
.ToArray() : null;
|
||||||
|
|
||||||
return _win32s.Cast<IProgram>()
|
return win32s.Cast<IProgram>()
|
||||||
.Concat(_uwps)
|
.Concat(uwps)
|
||||||
.AsParallel()
|
.AsParallel()
|
||||||
.WithCancellation(token)
|
.WithCancellation(token)
|
||||||
.Where(HideUninstallersFilter)
|
.Where(HideUninstallersFilter)
|
||||||
|
|
@ -109,11 +148,6 @@ namespace Flow.Launcher.Plugin.Program
|
||||||
{
|
{
|
||||||
return emptyResults;
|
return emptyResults;
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
_uwpsLock.Release();
|
|
||||||
_win32sLock.Release();
|
|
||||||
}
|
|
||||||
}, token);
|
}, token);
|
||||||
|
|
||||||
resultList = resultList.Count != 0 ? resultList : emptyResults;
|
resultList = resultList.Count != 0 ? resultList : emptyResults;
|
||||||
|
|
@ -275,7 +309,12 @@ namespace Flow.Launcher.Plugin.Program
|
||||||
|
|
||||||
var cacheEmpty = _win32sCount == 0 || _uwpsCount == 0;
|
var cacheEmpty = _win32sCount == 0 || _uwpsCount == 0;
|
||||||
|
|
||||||
if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now)
|
bool needReindex;
|
||||||
|
lock (_lastIndexTimeLock)
|
||||||
|
{
|
||||||
|
needReindex = _settings.LastIndexTime.AddHours(30) < DateTime.Now;
|
||||||
|
}
|
||||||
|
if (cacheEmpty || needReindex)
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
|
|
@ -308,8 +347,11 @@ namespace Flow.Launcher.Plugin.Program
|
||||||
}
|
}
|
||||||
ResetCache();
|
ResetCache();
|
||||||
await Context.API.SaveCacheBinaryStorageAsync<List<Win32>>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
|
await Context.API.SaveCacheBinaryStorageAsync<List<Win32>>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
|
||||||
|
lock (_lastIndexTimeLock)
|
||||||
|
{
|
||||||
_settings.LastIndexTime = DateTime.Now;
|
_settings.LastIndexTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Context.API.LogException(ClassName, "Failed to index Win32 programs", e);
|
Context.API.LogException(ClassName, "Failed to index Win32 programs", e);
|
||||||
|
|
@ -333,8 +375,11 @@ namespace Flow.Launcher.Plugin.Program
|
||||||
}
|
}
|
||||||
ResetCache();
|
ResetCache();
|
||||||
await Context.API.SaveCacheBinaryStorageAsync<List<UWPApp>>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
|
await Context.API.SaveCacheBinaryStorageAsync<List<UWPApp>>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
|
||||||
|
lock (_lastIndexTimeLock)
|
||||||
|
{
|
||||||
_settings.LastIndexTime = DateTime.Now;
|
_settings.LastIndexTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Context.API.LogException(ClassName, "Failed to index Uwp programs", e);
|
Context.API.LogException(ClassName, "Failed to index Uwp programs", e);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue