2022-08-09 00:35:38 +00:00
using System ;
2016-08-18 00:16:40 +00:00
using System.Collections.Generic ;
2016-08-22 01:21:22 +00:00
using System.Diagnostics ;
2016-08-18 00:16:40 +00:00
using System.IO ;
using System.Linq ;
using System.Security.Principal ;
2016-11-29 01:18:38 +00:00
using System.Threading.Tasks ;
2016-08-18 00:16:40 +00:00
using System.Windows.Media.Imaging ;
using Windows.ApplicationModel ;
2016-08-19 22:24:21 +00:00
using Windows.Management.Deployment ;
2020-04-21 09:12:17 +00:00
using Flow.Launcher.Infrastructure ;
using Flow.Launcher.Plugin.Program.Logger ;
2021-02-01 11:59:56 +00:00
using Flow.Launcher.Plugin.SharedModels ;
2022-02-21 21:54:46 +00:00
using System.Threading.Channels ;
2022-11-28 07:51:28 +00:00
using System.Xml ;
2022-11-28 11:06:16 +00:00
using Windows.ApplicationModel.Core ;
2016-08-18 00:16:40 +00:00
2020-04-21 09:12:17 +00:00
namespace Flow.Launcher.Plugin.Program.Programs
2016-08-18 00:16:40 +00:00
{
2017-01-13 01:21:00 +00:00
[Serializable]
2016-08-18 00:56:26 +00:00
public class UWP
2016-08-18 00:16:40 +00:00
{
public string Name { get ; }
public string FullName { get ; }
public string FamilyName { get ; }
public string Location { get ; set ; }
2022-11-28 07:51:28 +00:00
public Application [ ] Apps { get ; set ; } = Array . Empty < Application > ( ) ;
2016-08-18 00:16:40 +00:00
2016-11-29 01:18:38 +00:00
2016-08-18 00:56:26 +00:00
public UWP ( Package package )
2016-08-18 00:16:40 +00:00
{
2017-01-13 01:21:00 +00:00
Location = package . InstalledLocation . Path ;
Name = package . Id . Name ;
FullName = package . Id . FullName ;
FamilyName = package . Id . FamilyName ;
2022-11-28 07:51:28 +00:00
}
2022-11-28 17:22:13 +00:00
public void InitAppsInPackage ( Package package )
2022-11-28 07:51:28 +00:00
{
2022-12-31 15:43:07 +00:00
var apps = new List < Application > ( ) ;
2022-11-28 14:01:16 +00:00
// WinRT
var appListEntries = package . GetAppListEntries ( ) ;
foreach ( var app in appListEntries )
2022-11-28 07:51:28 +00:00
{
2022-11-28 14:01:16 +00:00
try
2022-11-28 07:51:28 +00:00
{
2022-11-28 14:01:16 +00:00
var tmp = new Application ( app , this ) ;
2022-12-31 15:43:07 +00:00
apps . Add ( tmp ) ;
2022-11-28 14:01:16 +00:00
}
catch ( Exception e )
{
2022-11-28 17:46:48 +00:00
ProgramLogger . LogException ( $"|UWP|InitAppsInPackage|{Location}" +
"|Unexpected exception occurs when trying to construct a Application from package"
+ $"{FullName} from location {Location}" , e ) ;
2022-11-28 07:51:28 +00:00
}
2022-11-28 14:01:16 +00:00
}
2022-12-31 15:43:07 +00:00
Apps = apps . ToArray ( ) ;
2022-11-28 10:46:48 +00:00
2022-11-28 14:01:16 +00:00
try
{
2022-11-28 10:46:48 +00:00
var xmlDoc = GetManifestXml ( ) ;
if ( xmlDoc = = null )
{
return ;
}
var xmlRoot = xmlDoc . DocumentElement ;
2022-11-28 14:01:16 +00:00
var packageVersion = GetPackageVersionFromManifest ( xmlRoot ) ;
2022-11-29 04:30:36 +00:00
if ( ! smallLogoNameFromVersion . TryGetValue ( packageVersion , out string logoName ) | |
! bigLogoNameFromVersion . TryGetValue ( packageVersion , out string bigLogoName ) )
2022-11-28 17:46:48 +00:00
{
return ;
}
2022-11-28 18:14:43 +00:00
2022-11-28 10:46:48 +00:00
var namespaceManager = new XmlNamespaceManager ( xmlDoc . NameTable ) ;
2022-11-29 09:49:36 +00:00
namespaceManager . AddNamespace ( "d" , "http://schemas.microsoft.com/appx/manifest/foundation/windows10" ) ; // still need a name
2022-11-28 10:46:48 +00:00
namespaceManager . AddNamespace ( "rescap" , "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" ) ;
namespaceManager . AddNamespace ( "uap10" , "http://schemas.microsoft.com/appx/manifest/uap/windows10/10" ) ;
2022-11-29 09:49:36 +00:00
var allowElevationNode = xmlRoot . SelectSingleNode ( "//rescap:Capability[@Name='allowElevation']" , namespaceManager ) ;
2022-11-28 10:46:48 +00:00
bool packageCanElevate = allowElevationNode ! = null ;
2022-11-29 09:49:36 +00:00
var appsNode = xmlRoot . SelectSingleNode ( "d:Applications" , namespaceManager ) ;
2022-11-28 10:46:48 +00:00
foreach ( var app in Apps )
{
// According to https://learn.microsoft.com/windows/apps/desktop/modernize/grant-identity-to-nonpackaged-apps#create-a-package-manifest-for-the-sparse-package
// and https://learn.microsoft.com/uwp/schemas/appxpackage/uapmanifestschema/element-application#attributes
var id = app . UserModelId . Split ( '!' ) [ 1 ] ;
2022-11-29 09:49:36 +00:00
var appNode = appsNode ? . SelectSingleNode ( $"d:Application[@Id='{id}']" , namespaceManager ) ;
2022-11-28 10:46:48 +00:00
if ( appNode ! = null )
{
2022-11-29 09:49:36 +00:00
app . CanRunElevated = packageCanElevate | | Application . IfAppCanRunElevated ( appNode ) ;
2022-11-28 17:46:48 +00:00
2022-11-29 09:49:36 +00:00
// local name to fit all versions
var visualElement = appNode . SelectSingleNode ( $"*[local-name()='VisualElements']" , namespaceManager ) ;
2022-11-28 18:06:11 +00:00
var logoUri = visualElement ? . Attributes [ logoName ] ? . Value ;
2022-11-29 09:50:49 +00:00
app . LogoPath = app . LogoPathFromUri ( logoUri , ( 64 , 64 ) ) ;
2022-12-07 07:18:57 +00:00
// use small logo or may have a big margin
var previewUri = visualElement ? . Attributes [ logoName ] ? . Value ;
app . PreviewImagePath = app . LogoPathFromUri ( previewUri , ( 256 , 256 ) ) ;
2022-11-28 10:46:48 +00:00
}
}
2022-11-28 07:51:28 +00:00
}
catch ( Exception e )
{
2022-11-28 17:46:48 +00:00
ProgramLogger . LogException ( $"|UWP|InitAppsInPackage|{Location}" +
"|Unexpected exception occurs when trying to construct a Application from package"
+ $"{FullName} from location {Location}" , e ) ;
2022-11-28 07:51:28 +00:00
}
}
private XmlDocument GetManifestXml ( )
{
var manifest = Path . Combine ( Location , "AppxManifest.xml" ) ;
try
{
var file = File . ReadAllText ( manifest ) ;
var xmlDoc = new XmlDocument ( ) ;
xmlDoc . LoadXml ( file ) ;
return xmlDoc ;
}
catch ( FileNotFoundException e )
{
ProgramLogger . LogException ( "UWP" , "GetManifestXml" , $"{Location}" , "AppxManifest.xml not found." , e ) ;
return null ;
}
catch ( Exception e )
{
2022-12-30 15:57:57 +00:00
ProgramLogger . LogException ( "UWP" , "GetManifestXml" , $"{Location}" , "An unexpected error occurred and unable to parse AppxManifest.xml" , e ) ;
2022-11-28 07:51:28 +00:00
return null ;
}
}
2022-11-29 09:49:36 +00:00
private PackageVersion GetPackageVersionFromManifest ( XmlNode xmlRoot )
2016-11-29 01:18:38 +00:00
{
2022-11-28 14:01:16 +00:00
if ( xmlRoot ! = null )
2016-11-29 01:18:38 +00:00
{
2022-11-28 14:01:16 +00:00
var namespaces = xmlRoot . Attributes ;
foreach ( XmlAttribute ns in namespaces )
{
if ( versionFromNamespace . TryGetValue ( ns . Value , out var packageVersion ) )
{
return packageVersion ;
}
}
ProgramLogger . LogException ( $"|UWP|GetPackageVersionFromManifest|{Location}" +
2022-12-31 22:12:43 +00:00
"|Trying to get the package version of the UWP program, but an unknown UWP app-manifest version in package "
2022-11-28 17:46:48 +00:00
+ $"{FullName} from location {Location}" , new FormatException ( ) ) ;
2022-11-28 14:01:16 +00:00
return PackageVersion . Unknown ;
2016-11-29 01:18:38 +00:00
}
else
{
2022-11-28 14:01:16 +00:00
ProgramLogger . LogException ( $"|UWP|GetPackageVersionFromManifest|{Location}" +
2022-11-28 17:46:48 +00:00
"|Can't parse AppManifest.xml of package "
+ $"{FullName} from location {Location}" , new ArgumentNullException ( nameof ( xmlRoot ) ) ) ;
2022-11-28 14:01:16 +00:00
return PackageVersion . Unknown ;
2016-11-29 01:18:38 +00:00
}
}
2022-11-28 14:01:16 +00:00
private static readonly Dictionary < string , PackageVersion > versionFromNamespace = new ( )
2016-11-29 01:18:38 +00:00
{
2022-01-08 21:50:27 +00:00
{
"http://schemas.microsoft.com/appx/manifest/foundation/windows10" , PackageVersion . Windows10
} ,
{
"http://schemas.microsoft.com/appx/2013/manifest" , PackageVersion . Windows81
} ,
{
"http://schemas.microsoft.com/appx/2010/manifest" , PackageVersion . Windows8
} ,
2022-11-28 14:01:16 +00:00
} ;
2016-11-29 01:18:38 +00:00
2022-11-29 04:30:36 +00:00
private static readonly Dictionary < PackageVersion , string > smallLogoNameFromVersion = new ( )
2022-11-28 14:01:16 +00:00
{
2016-11-29 01:18:38 +00:00
{
2022-11-28 14:01:16 +00:00
PackageVersion . Windows10 , "Square44x44Logo"
} ,
{
PackageVersion . Windows81 , "Square30x30Logo"
} ,
{
PackageVersion . Windows8 , "SmallLogo"
} ,
} ;
2016-09-05 20:58:40 +00:00
2022-11-29 04:30:36 +00:00
private static readonly Dictionary < PackageVersion , string > bigLogoNameFromVersion = new ( )
{
{
2022-12-07 06:56:34 +00:00
PackageVersion . Windows10 , "Square150x150Logo"
2022-11-29 04:30:36 +00:00
} ,
{
2022-12-07 06:56:34 +00:00
PackageVersion . Windows81 , "Square150x150Logo"
2022-11-29 04:30:36 +00:00
} ,
{
PackageVersion . Windows8 , "Logo"
} ,
} ;
2022-12-17 14:00:04 +00:00
public static Application [ ] All ( Settings settings )
2016-08-18 00:16:40 +00:00
{
2022-12-17 14:00:04 +00:00
var support = SupportUWP ( ) ;
2022-12-20 11:48:10 +00:00
if ( support & & settings . EnableUWP )
2016-08-18 00:16:40 +00:00
{
2017-04-01 18:15:23 +00:00
var applications = CurrentUserPackages ( ) . AsParallel ( ) . SelectMany ( p = >
{
UWP u ;
try
{
u = new UWP ( p ) ;
2022-11-28 17:22:13 +00:00
u . InitAppsInPackage ( p ) ;
2017-04-01 18:15:23 +00:00
}
2019-10-25 02:11:52 +00:00
#if ! DEBUG
2017-04-01 18:15:23 +00:00
catch ( Exception e )
{
2022-12-30 15:57:57 +00:00
ProgramLogger . LogException ( $"|UWP|All|{p.InstalledLocation}|An unexpected error occurred and unable to convert Package to UWP for {p.Id.FullName}" , e ) ;
2022-08-09 00:48:17 +00:00
return Array . Empty < Application > ( ) ;
2017-04-01 18:15:23 +00:00
}
2019-10-25 02:11:52 +00:00
#endif
#if DEBUG //make developer aware and implement handling
2019-11-16 00:37:01 +00:00
catch
2019-10-25 02:11:52 +00:00
{
2019-11-16 00:37:01 +00:00
throw ;
2019-10-25 02:11:52 +00:00
}
#endif
2017-04-01 18:15:23 +00:00
return u . Apps ;
} ) . ToArray ( ) ;
2019-09-09 21:57:03 +00:00
var updatedListWithoutDisabledApps = applications
2022-01-08 21:50:27 +00:00
. Where ( t1 = > ! Main . _settings . DisabledProgramSources
2022-10-24 17:13:36 +00:00
. Any ( x = > x . UniqueIdentifier = = t1 . UniqueIdentifier ) ) ;
2019-09-09 21:57:03 +00:00
return updatedListWithoutDisabledApps . ToArray ( ) ;
2016-08-20 00:17:28 +00:00
}
else
{
2022-10-21 10:35:10 +00:00
return Array . Empty < Application > ( ) ;
2016-08-20 00:17:28 +00:00
}
2016-08-18 00:16:40 +00:00
}
2022-12-17 14:00:04 +00:00
public static bool SupportUWP ( )
{
var windows10 = new Version ( 10 , 0 ) ;
var support = Environment . OSVersion . Version . Major > = windows10 . Major ;
return support ;
}
2016-08-18 00:16:40 +00:00
private static IEnumerable < Package > CurrentUserPackages ( )
{
2022-12-31 13:25:12 +00:00
var user = WindowsIdentity . GetCurrent ( ) . User ;
2016-08-18 00:16:40 +00:00
2022-12-31 13:25:12 +00:00
if ( user ! = null )
2016-08-18 00:16:40 +00:00
{
2022-12-31 13:25:12 +00:00
var userId = user . Value ;
PackageManager packageManager ;
2021-02-11 19:18:38 +00:00
try
{
2022-12-31 13:25:12 +00:00
packageManager = new PackageManager ( ) ;
2021-02-11 19:18:38 +00:00
}
catch
{
2021-02-23 19:34:09 +00:00
// Bug from https://github.com/microsoft/CsWinRT, using Microsoft.Windows.SDK.NET.Ref 10.0.19041.0.
// Only happens on the first time, so a try catch can fix it.
2022-12-31 13:25:12 +00:00
packageManager = new PackageManager ( ) ;
2021-02-11 19:18:38 +00:00
}
2022-12-31 13:25:12 +00:00
var packages = packageManager . FindPackagesForUser ( userId ) ;
packages = packages . Where ( p = >
2017-04-01 18:15:23 +00:00
{
2019-11-01 19:44:55 +00:00
try
{
var f = p . IsFramework ;
var d = p . IsDevelopmentMode ;
var path = p . InstalledLocation . Path ;
2022-11-28 17:22:13 +00:00
return ! f & & ! d & & ! string . IsNullOrEmpty ( path ) ;
2019-11-01 19:44:55 +00:00
}
catch ( Exception e )
{
2022-12-31 13:34:49 +00:00
ProgramLogger . LogException ( "UWP" , "CurrentUserPackages" , $"{p.Id.FullName}" , "An unexpected error occurred and "
2022-01-08 21:50:27 +00:00
+ $"unable to verify if package is valid" , e ) ;
2019-11-01 19:44:55 +00:00
return false ;
}
2017-04-01 18:15:23 +00:00
} ) ;
2022-12-31 13:25:12 +00:00
return packages ;
2016-08-18 00:16:40 +00:00
}
else
{
2022-10-21 10:35:10 +00:00
return Array . Empty < Package > ( ) ;
2016-08-18 00:16:40 +00:00
}
}
2022-02-21 21:54:46 +00:00
private static Channel < byte > PackageChangeChannel = Channel . CreateBounded < byte > ( 1 ) ;
2022-01-08 21:50:27 +00:00
2022-02-21 21:54:46 +00:00
public static async Task WatchPackageChange ( )
2022-01-08 21:50:27 +00:00
{
2022-02-21 21:54:46 +00:00
if ( Environment . OSVersion . Version . Major > = 10 )
{
var catalog = PackageCatalog . OpenForCurrentUser ( ) ;
catalog . PackageInstalling + = ( _ , args ) = >
{
if ( args . IsComplete )
PackageChangeChannel . Writer . TryWrite ( default ) ;
} ;
catalog . PackageUninstalling + = ( _ , args ) = >
2022-01-08 22:03:53 +00:00
{
2022-02-21 21:54:46 +00:00
if ( args . IsComplete )
PackageChangeChannel . Writer . TryWrite ( default ) ;
2022-01-08 22:03:53 +00:00
} ;
2022-02-21 21:54:46 +00:00
catalog . PackageUpdating + = ( _ , args ) = >
{
if ( args . IsComplete )
PackageChangeChannel . Writer . TryWrite ( default ) ;
} ;
while ( await PackageChangeChannel . Reader . WaitToReadAsync ( ) . ConfigureAwait ( false ) )
{
await Task . Delay ( 3000 ) . ConfigureAwait ( false ) ;
PackageChangeChannel . Reader . TryRead ( out _ ) ;
await Task . Run ( Main . IndexUwpPrograms ) ;
}
2022-11-28 11:06:16 +00:00
2022-02-21 21:54:46 +00:00
}
2022-01-08 21:50:27 +00:00
}
2016-08-18 00:16:40 +00:00
public override string ToString ( )
{
return FamilyName ;
}
public override bool Equals ( object obj )
{
2019-11-16 00:37:01 +00:00
if ( obj is UWP uwp )
2016-08-18 00:16:40 +00:00
{
return FamilyName . Equals ( uwp . FamilyName ) ;
}
else
{
return false ;
}
}
public override int GetHashCode ( )
{
return FamilyName . GetHashCode ( ) ;
}
2017-01-13 01:21:00 +00:00
[Serializable]
2016-08-22 01:21:22 +00:00
public class Application : IProgram
2016-08-18 00:16:40 +00:00
{
2022-11-14 16:07:12 +00:00
private string _uid = string . Empty ;
public string UniqueIdentifier { get = > _uid ; set = > _uid = value = = null ? string . Empty : value . ToLowerInvariant ( ) ; }
2022-11-28 14:13:55 +00:00
public string DisplayName { get ; set ; } = string . Empty ;
public string Description { get ; set ; } = string . Empty ;
public string UserModelId { get ; set ; } = string . Empty ;
2022-11-29 04:12:09 +00:00
//public string BackgroundColor { get; set; } = string.Empty; // preserve for future use
2019-10-17 11:47:00 +00:00
public string Name = > DisplayName ;
2022-11-28 14:13:55 +00:00
public string Location { get ; set ; } = string . Empty ;
2019-10-17 11:47:00 +00:00
2022-11-28 14:08:21 +00:00
public bool Enabled { get ; set ; } = false ;
2022-11-28 07:51:28 +00:00
public bool CanRunElevated { get ; set ; } = false ;
2022-11-28 14:01:16 +00:00
public string LogoPath { get ; set ; } = string . Empty ;
2022-11-29 04:30:36 +00:00
public string PreviewImagePath { get ; set ; } = string . Empty ;
2022-11-28 07:51:28 +00:00
2022-11-28 11:06:16 +00:00
public Application ( AppListEntry appListEntry , UWP package )
2022-11-28 10:43:56 +00:00
{
UserModelId = appListEntry . AppUserModelId ;
UniqueIdentifier = appListEntry . AppUserModelId ;
DisplayName = appListEntry . DisplayInfo . DisplayName ;
Description = appListEntry . DisplayInfo . Description ;
2022-11-28 11:06:16 +00:00
Location = package . Location ;
2022-11-28 10:43:56 +00:00
Enabled = true ;
}
2016-08-22 01:21:22 +00:00
public Result Result ( string query , IPublicAPI api )
2016-08-18 00:16:40 +00:00
{
2021-02-01 11:57:08 +00:00
string title ;
MatchResult matchResult ;
2020-10-25 03:26:54 +00:00
2021-02-01 11:57:08 +00:00
// We suppose Name won't be null
2023-01-10 06:18:12 +00:00
if ( ! Main . _settings . EnableDescription | | string . IsNullOrWhiteSpace ( Description ) | | Name . Equals ( Description ) )
2021-02-01 11:57:08 +00:00
{
title = Name ;
2023-01-10 06:18:12 +00:00
matchResult = StringMatcher . FuzzySearch ( query , Name ) ;
2021-02-01 11:57:08 +00:00
}
else
{
title = $"{Name}: {Description}" ;
var nameMatch = StringMatcher . FuzzySearch ( query , Name ) ;
2022-12-31 15:43:07 +00:00
var descriptionMatch = StringMatcher . FuzzySearch ( query , Description ) ;
if ( descriptionMatch . Score > nameMatch . Score )
2021-02-01 11:57:08 +00:00
{
2022-12-31 15:43:07 +00:00
for ( int i = 0 ; i < descriptionMatch . MatchData . Count ; i + + )
2021-02-01 11:57:08 +00:00
{
2022-12-31 15:43:07 +00:00
descriptionMatch . MatchData [ i ] + = Name . Length + 2 ; // 2 is ": "
2021-02-01 11:57:08 +00:00
}
2022-12-31 15:43:07 +00:00
matchResult = descriptionMatch ;
2021-02-01 11:57:08 +00:00
}
2023-01-10 06:18:12 +00:00
else
{
matchResult = nameMatch ;
}
2021-02-01 11:57:08 +00:00
}
2020-10-25 03:26:54 +00:00
2023-01-10 06:18:12 +00:00
if ( ! matchResult . IsSearchPrecisionScoreMet ( ) )
2021-02-02 09:28:52 +00:00
return null ;
2019-11-28 23:38:50 +00:00
2016-08-22 01:21:22 +00:00
var result = new Result
{
2020-10-25 03:26:54 +00:00
Title = title ,
2023-01-26 04:31:40 +00:00
AutoCompleteText = Name ,
2022-11-28 11:06:16 +00:00
SubTitle = Main . _settings . HideAppsPath ? string . Empty : Location ,
2022-11-28 14:55:10 +00:00
IcoPath = LogoPath ,
2022-12-05 08:01:20 +00:00
Preview = new Result . PreviewInfo
{
IsMedia = false ,
PreviewImagePath = PreviewImagePath ,
Description = Description
} ,
2020-10-25 03:26:54 +00:00
Score = matchResult . Score ,
TitleHighlightData = matchResult . MatchData ,
2016-08-22 01:21:22 +00:00
ContextData = this ,
Action = e = >
{
2021-09-11 12:30:30 +00:00
var elevated = (
e . SpecialKeyState . CtrlPressed & &
e . SpecialKeyState . ShiftPressed & &
! e . SpecialKeyState . AltPressed & &
! e . SpecialKeyState . WinPressed
2022-01-08 21:50:27 +00:00
) ;
2021-09-11 12:30:30 +00:00
2022-11-28 17:02:53 +00:00
bool shouldRunElevated = elevated & & CanRunElevated ;
_ = Task . Run ( ( ) = > Launch ( shouldRunElevated ) ) . ConfigureAwait ( false ) ;
if ( elevated & & ! shouldRunElevated )
2021-09-11 12:30:30 +00:00
{
2022-11-29 10:13:55 +00:00
var title = api . GetTranslation ( "flowlauncher_plugin_program_disable_dlgtitle_error" ) ;
2022-11-28 17:02:53 +00:00
var message = api . GetTranslation ( "flowlauncher_plugin_program_run_as_administrator_not_supported_message" ) ;
api . ShowMsg ( title , message , string . Empty ) ;
2021-09-11 12:30:30 +00:00
}
2016-08-22 01:21:22 +00:00
return true ;
}
} ;
2020-10-25 03:26:54 +00:00
2016-08-22 01:21:22 +00:00
return result ;
}
public List < Result > ContextMenus ( IPublicAPI api )
{
var contextMenus = new List < Result >
{
new Result
{
2020-04-21 12:54:41 +00:00
Title = api . GetTranslation ( "flowlauncher_plugin_program_open_containing_folder" ) ,
2016-08-22 01:21:22 +00:00
Action = _ = >
{
2022-11-28 11:06:16 +00:00
Main . Context . API . OpenDirectory ( Location ) ;
2019-12-16 10:01:45 +00:00
return true ;
2016-08-22 01:21:22 +00:00
} ,
IcoPath = "Images/folder.png"
}
} ;
2021-09-11 12:30:30 +00:00
if ( CanRunElevated )
{
contextMenus . Add ( new Result
{
Title = api . GetTranslation ( "flowlauncher_plugin_program_run_as_administrator" ) ,
Action = _ = >
{
2022-11-28 17:02:53 +00:00
Task . Run ( ( ) = > Launch ( true ) ) . ConfigureAwait ( false ) ;
2021-09-11 12:30:30 +00:00
return true ;
} ,
IcoPath = "Images/cmd.png"
} ) ;
}
2016-08-22 01:21:22 +00:00
return contextMenus ;
}
2022-11-28 18:14:43 +00:00
private void Launch ( bool elevated = false )
2021-09-11 12:30:30 +00:00
{
2022-11-28 17:02:53 +00:00
string command = "shell:AppsFolder\\" + UserModelId ;
2021-09-11 12:30:30 +00:00
command = Environment . ExpandEnvironmentVariables ( command . Trim ( ) ) ;
var info = new ProcessStartInfo ( command )
{
2022-11-28 11:06:16 +00:00
UseShellExecute = true ,
2022-11-28 17:02:53 +00:00
Verb = elevated ? "runas" : ""
2021-09-11 12:30:30 +00:00
} ;
Main . StartProcess ( Process . Start , info ) ;
}
2022-11-29 09:49:36 +00:00
internal static bool IfAppCanRunElevated ( XmlNode appNode )
2022-11-28 10:43:56 +00:00
{
// According to https://learn.microsoft.com/windows/apps/desktop/modernize/grant-identity-to-nonpackaged-apps#create-a-package-manifest-for-the-sparse-package
// and https://learn.microsoft.com/uwp/schemas/appxpackage/uapmanifestschema/element-application#attributes
2022-11-29 09:49:36 +00:00
return appNode ? . Attributes [ "EntryPoint" ] ? . Value = = "Windows.FullTrustApplication" | |
appNode ? . Attributes [ "uap10:TrustLevel" ] ? . Value = = "mediumIL" ;
2022-11-28 10:43:56 +00:00
}
2022-11-29 04:30:36 +00:00
internal string LogoPathFromUri ( string uri , ( int , int ) desiredSize )
2016-11-29 01:46:29 +00:00
{
// all https://msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets
// windows 10 https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx
// windows 8.1 https://msdn.microsoft.com/en-us/library/windows/apps/hh965372.aspx#target_size
// windows 8 https://msdn.microsoft.com/en-us/library/windows/apps/br211475.aspx
2022-11-28 14:01:16 +00:00
if ( string . IsNullOrWhiteSpace ( uri ) )
{
2022-11-28 17:48:04 +00:00
ProgramLogger . LogException ( $"|UWP|LogoPathFromUri|{Location}" +
$"|{UserModelId} 's logo uri is null or empty: {Location}" , new ArgumentException ( "uri" ) ) ;
return string . Empty ;
2022-11-28 14:01:16 +00:00
}
2022-11-28 11:06:16 +00:00
string path = Path . Combine ( Location , uri ) ;
2022-10-19 18:30:58 +00:00
2022-11-29 04:30:36 +00:00
var pxCount = desiredSize . Item1 * desiredSize . Item2 ;
var logoPath = TryToFindLogo ( uri , path , pxCount ) ;
2022-11-28 17:48:04 +00:00
if ( logoPath = = string . Empty )
2016-11-29 01:46:29 +00:00
{
2022-11-28 11:06:16 +00:00
var tmp = Path . Combine ( Location , "Assets" , uri ) ;
2022-11-28 09:48:27 +00:00
if ( ! path . Equals ( tmp , StringComparison . OrdinalIgnoreCase ) )
{
2022-11-28 11:06:16 +00:00
// TODO: Don't know why, just keep it at the moment
// Maybe on older version of Windows 10?
// for C:\Windows\MiracastView etc
2022-11-29 04:30:36 +00:00
return TryToFindLogo ( uri , tmp , pxCount ) ;
2022-11-28 09:48:27 +00:00
}
2016-11-29 01:46:29 +00:00
}
2022-11-28 17:48:04 +00:00
return logoPath ;
2016-11-29 01:46:29 +00:00
2022-11-29 04:30:36 +00:00
string TryToFindLogo ( string uri , string path , int px )
2016-11-29 01:46:29 +00:00
{
2022-10-20 17:01:07 +00:00
var extension = Path . GetExtension ( path ) ;
if ( extension ! = null )
2022-01-08 21:50:27 +00:00
{
2022-10-28 08:36:44 +00:00
//if (File.Exists(path))
//{
// return path; // shortcut, avoid enumerating files
//}
2016-11-29 01:46:29 +00:00
2022-10-20 17:01:07 +00:00
var logoNamePrefix = Path . GetFileNameWithoutExtension ( uri ) ; // e.g Square44x44
var logoDir = Path . GetDirectoryName ( path ) ; // e.g ..\..\Assets
2022-11-28 18:06:11 +00:00
if ( String . IsNullOrEmpty ( logoNamePrefix ) | | ! Directory . Exists ( logoDir ) )
2022-01-08 21:50:27 +00:00
{
2022-10-20 17:01:07 +00:00
// Known issue: Edge always triggers it since logo is not at uri
2022-11-28 11:06:16 +00:00
ProgramLogger . LogException ( $"|UWP|LogoPathFromUri|{Location}" +
$"|{UserModelId} can't find logo uri for {uri} in package location (logo name or directory not found): {Location}" , new FileNotFoundException ( ) ) ;
2022-10-20 17:01:07 +00:00
return string . Empty ;
}
2022-11-29 06:34:15 +00:00
var logos = Directory . EnumerateFiles ( logoDir , $"{logoNamePrefix}*{extension}" ) ;
2022-10-20 17:01:07 +00:00
// Currently we don't care which one to choose
// Just ignore all qualifiers
// select like logo.[xxx_yyy].png
// https://learn.microsoft.com/en-us/windows/uwp/app-resources/tailor-resources-lang-scale-contrast
2022-12-07 07:18:57 +00:00
// todo select from file name like pt run
2022-10-28 08:36:44 +00:00
var selected = logos . FirstOrDefault ( ) ;
2022-10-31 12:03:34 +00:00
var closest = selected ;
int min = int . MaxValue ;
2022-11-28 11:06:16 +00:00
foreach ( var logo in logos )
2022-01-08 21:50:27 +00:00
{
2022-10-28 08:36:44 +00:00
var imageStream = File . OpenRead ( logo ) ;
var decoder = BitmapDecoder . Create ( imageStream , BitmapCreateOptions . IgnoreColorProfile , BitmapCacheOption . None ) ;
var height = decoder . Frames [ 0 ] . PixelHeight ;
var width = decoder . Frames [ 0 ] . PixelWidth ;
2022-11-29 04:30:36 +00:00
int pixelCountDiff = Math . Abs ( height * width - px ) ;
2022-11-28 11:06:16 +00:00
if ( pixelCountDiff < min )
2022-01-08 21:50:27 +00:00
{
2022-11-29 04:30:36 +00:00
// try to find the closest to desired size
2022-10-31 12:03:34 +00:00
closest = logo ;
if ( pixelCountDiff = = 0 )
2022-11-29 04:30:36 +00:00
break ; // found
2022-10-31 12:03:34 +00:00
min = pixelCountDiff ;
2022-01-08 21:50:27 +00:00
}
}
2018-07-15 03:32:21 +00:00
2022-10-31 12:03:34 +00:00
selected = closest ;
2022-10-20 17:01:07 +00:00
if ( ! string . IsNullOrEmpty ( selected ) )
2018-07-15 03:32:21 +00:00
{
2022-10-20 17:01:07 +00:00
return selected ;
}
else
{
2022-11-28 11:06:16 +00:00
ProgramLogger . LogException ( $"|UWP|LogoPathFromUri|{Location}" +
$"|{UserModelId} can't find logo uri for {uri} in package location (can't find specified logo): {Location}" , new FileNotFoundException ( ) ) ;
2022-10-20 17:01:07 +00:00
return string . Empty ;
2018-07-15 03:32:21 +00:00
}
2016-11-29 01:46:29 +00:00
}
else
{
2022-11-28 11:06:16 +00:00
ProgramLogger . LogException ( $"|UWP|LogoPathFromUri|{Location}" +
2022-10-20 17:01:07 +00:00
$"|Unable to find extension from {uri} for {UserModelId} " +
2022-11-28 11:06:16 +00:00
$"in package location {Location}" , new FileNotFoundException ( ) ) ;
2016-11-29 01:46:29 +00:00
return string . Empty ;
}
}
}
2016-08-18 00:16:40 +00:00
2022-11-29 04:12:09 +00:00
#region logo legacy
// preserve for potential future use
//public ImageSource Logo()
//{
// var logo = ImageFromPath(LogoPath);
// var plated = PlatedImage(logo); // TODO: maybe get plated directly from app package?
// // todo magic! temp fix for cross thread object
// plated.Freeze();
// return plated;
//}
//private BitmapImage ImageFromPath(string path)
//{
// if (File.Exists(path))
// {
// var image = new BitmapImage();
// image.BeginInit();
// image.UriSource = new Uri(path);
// image.CacheOption = BitmapCacheOption.OnLoad;
// image.EndInit();
// image.Freeze();
// return image;
// }
// else
// {
2022-12-31 22:12:43 +00:00
// ProgramLogger.LogException($"|UWP|ImageFromPath|{(string.IsNullOrEmpty(path) ? "Not Available" : path)}" +
2022-11-29 04:12:09 +00:00
// $"|Unable to get logo for {UserModelId} from {path} and" +
// $" located in {Location}", new FileNotFoundException());
// return new BitmapImage(new Uri(Constant.MissingImgIcon));
// }
//}
//private ImageSource PlatedImage(BitmapImage image)
//{
// if (!string.IsNullOrEmpty(BackgroundColor) && BackgroundColor != "transparent")
// {
// var width = image.Width;
// var height = image.Height;
// var x = 0;
// var y = 0;
// var group = new DrawingGroup();
// var converted = ColorConverter.ConvertFromString(BackgroundColor);
// if (converted != null)
// {
// var color = (Color)converted;
// var brush = new SolidColorBrush(color);
// var pen = new Pen(brush, 1);
// var backgroundArea = new Rect(0, 0, width, width);
2022-12-31 15:43:07 +00:00
// var rectangle = new RectangleGeometry(backgroundArea);
// var rectDrawing = new GeometryDrawing(brush, pen, rectangle);
2022-11-29 04:12:09 +00:00
// group.Children.Add(rectDrawing);
// var imageArea = new Rect(x, y, image.Width, image.Height);
// var imageDrawing = new ImageDrawing(image, imageArea);
// group.Children.Add(imageDrawing);
// // http://stackoverflow.com/questions/6676072/get-system-drawing-bitmap-of-a-wpf-area-using-visualbrush
// var visual = new DrawingVisual();
// var context = visual.RenderOpen();
// context.DrawDrawing(group);
// context.Close();
// const int dpiScale100 = 96;
// var bitmap = new RenderTargetBitmap(
// Convert.ToInt32(width), Convert.ToInt32(height),
// dpiScale100, dpiScale100,
// PixelFormats.Pbgra32
// );
// bitmap.Render(visual);
// return bitmap;
// }
// else
// {
// ProgramLogger.LogException($"|UWP|PlatedImage|{Location}" +
// $"|Unable to convert background string {BackgroundColor} " +
// $"to color for {Location}", new InvalidOperationException());
// return new BitmapImage(new Uri(Constant.MissingImgIcon));
// }
// }
// else
// {
// // todo use windows theme as background
// return image;
// }
//}
#endregion
2016-08-18 00:16:40 +00:00
public override string ToString ( )
{
return $"{DisplayName}: {Description}" ;
}
2022-11-03 16:47:52 +00:00
public override bool Equals ( object obj )
{
if ( obj is Application other )
{
return UniqueIdentifier = = other . UniqueIdentifier ;
}
else
{
return false ;
}
}
public override int GetHashCode ( )
{
return UniqueIdentifier . GetHashCode ( ) ;
}
2016-08-18 00:16:40 +00:00
}
2016-11-29 01:18:38 +00:00
public enum PackageVersion
{
Windows10 ,
Windows81 ,
Windows8 ,
Unknown
}
2016-08-18 00:16:40 +00:00
}
2022-08-09 00:35:38 +00:00
}