diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 4bef98cf2..2f54813e1 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -38,6 +38,7 @@ + diff --git a/Flow.Launcher.Test/Plugins/ProgramTest.cs b/Flow.Launcher.Test/Plugins/ProgramTest.cs new file mode 100644 index 000000000..a0d2243ce --- /dev/null +++ b/Flow.Launcher.Test/Plugins/ProgramTest.cs @@ -0,0 +1,30 @@ +using Flow.Launcher.Plugin.Program.Programs; +using NUnit.Framework; +using System; +using Windows.ApplicationModel; + +namespace Flow.Launcher.Test.Plugins +{ + [TestFixture] + public class ProgramTest + { + [TestCase("Microsoft.WindowsCamera", "ms-resource:LensSDK/Resources/AppTitle", "ms-resource://Microsoft.WindowsCamera/LensSDK/Resources/AppTitle")] + [TestCase("microsoft.windowscommunicationsapps", "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName", + "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName")] + [TestCase("windows.immersivecontrolpanel", "ms-resource:DisplayName", "ms-resource://windows.immersivecontrolpanel/Resources/DisplayName")] + [TestCase("Microsoft.MSPaint", "ms-resource:AppName", "ms-resource://Microsoft.MSPaint/Resources/AppName")] + public void WhenGivenPriReferenceValueShouldReturnCorrectFormat(string packageName, string rawPriReferenceValue, string expectedFormat) + { + // Arrange + var app = new UWP.Application(); + + // Act + var result = app.FormattedPriReferenceValue(packageName, rawPriReferenceValue); + + // Assert + Assert.IsTrue(result == expectedFormat, + $"Expected Pri reference format: {expectedFormat}{Environment.NewLine} " + + $"Actual: {result}{Environment.NewLine}"); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 927c8784c..b8633f357 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -263,6 +263,8 @@ namespace Flow.Launcher.Plugin.Program.Programs public string LogoPath { get; set; } public UWP Package { get; set; } + public Application(){} + private int Score(string query) { var displayNameMatch = StringMatcher.FuzzySearch(query, DisplayName); @@ -363,73 +365,70 @@ namespace Flow.Launcher.Plugin.Program.Programs BackgroundColor = manifestApp.GetStringValue("BackgroundColor"); Package = package; - DisplayName = ResourceFromPri(package.FullName, DisplayName); - Description = ResourceFromPri(package.FullName, Description); + DisplayName = ResourceFromPri(package.FullName, package.Name, DisplayName); + Description = ResourceFromPri(package.FullName, package.Name, Description); LogoUri = LogoUriFromManifest(manifestApp); LogoPath = LogoPathFromUri(LogoUri); Enabled = true; } - internal string ResourceFromPri(string packageFullName, string resourceReference) + internal string ResourceFromPri(string packageFullName, string packageName, string rawReferenceValue) { - const string prefix = "ms-resource:"; - if (!string.IsNullOrWhiteSpace(resourceReference) && resourceReference.StartsWith(prefix)) - { - // magic comes from @talynone - // https://github.com/talynone/Wox.Plugin.WindowsUniversalAppLauncher/blob/master/StoreAppLauncher/Helpers/NativeApiHelper.cs#L139-L153 - string key = resourceReference.Substring(prefix.Length); - string parsed; - if (key.StartsWith("//")) - { - parsed = prefix + key; - } - else if (key.StartsWith("/")) - { - parsed = prefix + "//" + key; - } - else - { - parsed = prefix + "///resources/" + key; - } + if (string.IsNullOrWhiteSpace(rawReferenceValue) || !rawReferenceValue.StartsWith("ms-resource:")) + return rawReferenceValue; - var outBuffer = new StringBuilder(128); - string source = $"@{{{packageFullName}? {parsed}}}"; - var capacity = (uint)outBuffer.Capacity; - var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero); - if (hResult == Hresult.Ok) + var formattedPriReference = FormattedPriReferenceValue(packageName, rawReferenceValue); + + var outBuffer = new StringBuilder(128); + string source = $"@{{{packageFullName}? {formattedPriReference}}}"; + var capacity = (uint)outBuffer.Capacity; + var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero); + if (hResult == Hresult.Ok) + { + var loaded = outBuffer.ToString(); + if (!string.IsNullOrEmpty(loaded)) { - var loaded = outBuffer.ToString(); - if (!string.IsNullOrEmpty(loaded)) - { - return loaded; - } - else - { - ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Can't load null or empty result " - + $"pri {source} in uwp location {Package.Location}", new NullReferenceException()); - return string.Empty; - } + return loaded; } else { - // https://github.com/Wox-launcher/Wox/issues/964 - // known hresult 2147942522: - // 'Microsoft Corporation' violates pattern constraint of '\bms-resource:.{1,256}'. - // for - // Microsoft.MicrosoftOfficeHub_17.7608.23501.0_x64__8wekyb3d8bbwe: ms-resource://Microsoft.MicrosoftOfficeHub/officehubintl/AppManifest_GetOffice_Description - // Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription - var e = Marshal.GetExceptionForHR((int)hResult); - ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Load pri failed {source} with HResult {hResult} and location {Package.Location}", e); + ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Can't load null or empty result " + + $"pri {source} in uwp location {Package.Location}", new NullReferenceException()); return string.Empty; } } else { - return resourceReference; + var e = Marshal.GetExceptionForHR((int)hResult); + ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Load pri failed {source} with HResult {hResult} and location {Package.Location}", e); + return string.Empty; } } + public string FormattedPriReferenceValue(string packageName, string rawPriReferenceValue) + { + const string prefix = "ms-resource:"; + + if (string.IsNullOrWhiteSpace(rawPriReferenceValue) || !rawPriReferenceValue.StartsWith(prefix)) + return rawPriReferenceValue; + + string key = rawPriReferenceValue.Substring(prefix.Length); + if (key.StartsWith("//")) + return $"{prefix}{key}"; + + if (!key.StartsWith("/")) + { + key = $"/{key}"; + } + + if (!key.ToLower().Contains("resources")) + { + key = $"/Resources{key}"; + } + + return $"{prefix}//{packageName}{key}"; + } internal string LogoUriFromManifest(IAppxManifestApplication app) { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index a501d8ac5..cdea767f3 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -342,7 +342,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var paths = listToAdd.Distinct().ToArray(); var programs1 = paths.AsParallel().Where(p => Extension(p) == ExeExtension).Select(ExeProgram); - var programs2 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(ExeProgram); + var programs2 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(LnkProgram); var programs3 = from p in paths.AsParallel() let e = Extension(p) where e != ShortcutExtension && e != ExeExtension