Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workspaces] Implement PWA recognition, launch. #35913

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4ccce8f
[Workspaces] PWA: first steps: implement PWA app searcher, add basic …
donlaci Oct 15, 2024
3168e8b
spell checker
donlaci Oct 15, 2024
15fdbf0
Snapshot tool: adding command line args for edge
donlaci Oct 21, 2024
4ca467b
PWA: add icon handling, add launch of PWA
donlaci Nov 6, 2024
3892ba3
Impllement Aumid getters and comparison to connect PWA windows and pr…
donlaci Nov 13, 2024
5941a5b
Minor fixes, simplifications
donlaci Nov 13, 2024
1635a1d
Spell checker
donlaci Nov 13, 2024
461186a
Removing manual PWA selection, spell checker
donlaci Nov 13, 2024
2b9e985
Merge remote-tracking branch 'origin/main' into Workspaces_PWA
Nov 13, 2024
2686c57
Fix merge conflict
donlaci Nov 13, 2024
eeb6b51
Trying to convince spell checker, that "PEB" is a correct word.
donlaci Nov 13, 2024
a6d3e4e
XAML format fix
donlaci Nov 13, 2024
0752331
Extending snapshot tool by logs for better testablility
donlaci Nov 14, 2024
20c0f8c
spell checker fix
donlaci Nov 14, 2024
d758fa7
extending logs
donlaci Nov 15, 2024
1e0872d
extending logs
donlaci Nov 15, 2024
7d0b589
Removing some logs, modifying search criteria for pwa helper process …
donlaci Nov 15, 2024
af2ae83
extending PWA detection for the case the directory with the app-id is…
donlaci Nov 15, 2024
dfea224
Fix issue when pwaAppId is null
donlaci Nov 18, 2024
2278956
fix missing pwa-app-id handling in the editor. Removed unused propert…
donlaci Nov 18, 2024
4cbf7a4
Code cleaning: Moving duplicate code to a common project
donlaci Nov 18, 2024
383846d
Fix issue: adding new Guid as app id if it is empty
donlaci Nov 18, 2024
8ad3ca6
Code cleanup: moving Pwa related code from snapshotUtils to PwaHelper
donlaci Nov 18, 2024
e2230c8
Code cleaning
donlaci Nov 19, 2024
09f9661
Code cleanup: Move common Application model to Csharp Library
donlaci Nov 19, 2024
0359d95
code cleanup
donlaci Nov 19, 2024
9494e76
modifying package name
donlaci Nov 19, 2024
fb5316d
Ading project reference to Common.UI
donlaci Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ CRH
critsec
Crossdevice
CRSEL
crx
crw
CSearch
CSettings
Expand Down Expand Up @@ -639,6 +640,7 @@ HWNDLAST
HWNDNEXT
HWNDPREV
hyjiacan
IApp
IBeam
ICapture
IClass
Expand Down Expand Up @@ -1140,7 +1142,7 @@ pdo
pdto
pdtobj
pdw
Peb
peb
pef
PElems
Pels
Expand Down Expand Up @@ -1627,6 +1629,7 @@ tkconverters
TLayout
tlb
tlbimp
tlhelp
TMPVAR
TNP
Toolhelp
Expand Down
2 changes: 2 additions & 0 deletions src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public struct WindowPositionWrapper

public string AppUserModelId { get; set; }

public string PwaAppId { get; set; }
SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved

public string CommandLineArguments { get; set; }

public bool IsElevated { get; set; }
Expand Down
40 changes: 39 additions & 1 deletion src/modules/Workspaces/WorkspacesEditor/Models/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
Expand All @@ -16,6 +17,8 @@

using ManagedCommon;
using Windows.Management.Deployment;
using Windows.UI.Xaml;
using WorkspacesEditor.ViewModels;

namespace WorkspacesEditor.Models
{
Expand All @@ -37,6 +40,7 @@ public Application(Application other)
AppTitle = other.AppTitle;
PackageFullName = other.PackageFullName;
AppUserModelId = other.AppUserModelId;
PwaAppId = other.PwaAppId;
CommandLineArguments = other.CommandLineArguments;
IsElevated = other.IsElevated;
CanLaunchElevated = other.CanLaunchElevated;
Expand Down Expand Up @@ -110,6 +114,8 @@ public override int GetHashCode()

public string CommandLineArguments { get; set; }

public string PwaAppId { get; set; }

private bool _isElevated;

public bool IsElevated
Expand Down Expand Up @@ -241,10 +247,32 @@ public Icon Icon
var iconHandle = bitmap.GetHicon();
_icon = Icon.FromHandle(iconHandle);
}
else
else if (MainViewModel.IsPwaApp(this))
{
string iconFilename = MainViewModel.GetPwaIconFilename(this);
if (iconFilename != null)
{
Bitmap bitmap;
if (iconFilename.EndsWith("ico", StringComparison.InvariantCultureIgnoreCase))
{
bitmap = new Bitmap(iconFilename);
}
else
{
bitmap = (Bitmap)Image.FromFile(iconFilename);
}

var iconHandle = bitmap.GetHicon();
_icon = Icon.FromHandle(iconHandle);
}
}

if (_icon == null)
{
_icon = Icon.ExtractAssociatedIcon(AppPath);
}

IsNotFound = false;
}
catch (Exception)
{
Expand Down Expand Up @@ -474,5 +502,15 @@ internal void MinimizedChecked()
{
Maximized = false;
}

internal bool IsEdge()
{
return AppPath.EndsWith("edge.exe", StringComparison.InvariantCultureIgnoreCase);
}

internal bool IsChrome()
{
return AppPath.EndsWith("chrome.exe", StringComparison.InvariantCultureIgnoreCase);
}
}
}
1 change: 1 addition & 0 deletions src/modules/Workspaces/WorkspacesEditor/Models/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ public Project(ProjectData.ProjectWrapper project)
AppName = app.Application,
AppPath = app.ApplicationPath,
AppTitle = app.Title,
PwaAppId = app.PwaAppId,
PackageFullName = app.PackageFullName,
AppUserModelId = app.AppUserModelId,
Parent = this,
Expand Down
8 changes: 4 additions & 4 deletions src/modules/Workspaces/WorkspacesEditor/Utils/DrawHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@ Rectangle GetAppRect(Application app)

foreach (Application app in appsIncluded)
{
if (repeatCounter.TryGetValue(app.AppPath, out int value))
if (repeatCounter.TryGetValue(app.AppPath + app.AppTitle, out int value))
{
repeatCounter[app.AppPath] = ++value;
repeatCounter[app.AppPath + app.AppTitle] = ++value;
}
else
{
repeatCounter.Add(app.AppPath, 1);
repeatCounter.Add(app.AppPath + app.AppTitle, 1);
}

app.RepeatIndex = repeatCounter[app.AppPath];
app.RepeatIndex = repeatCounter[app.AppPath + app.AppTitle];
}

foreach (Application app in project.Applications.Where(x => !x.IsIncluded))
Expand Down
23 changes: 23 additions & 0 deletions src/modules/Workspaces/WorkspacesEditor/Utils/PwaApp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WorkspacesEditor.Utils
{
public class PwaApp
{
public string Name { get; set; }

public string ShortcutFilename { get; set; }
SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved

public string IconFilename { get; set; }

public string AppId { get; set; }
}
}
110 changes: 110 additions & 0 deletions src/modules/Workspaces/WorkspacesEditor/Utils/PwaHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Win32;

namespace WorkspacesEditor.Utils
{
public class PwaHelper
{
private const string ChromeBase = "Google\\Chrome\\User Data\\Default\\Web Applications";
private const string EdgeBase = "Microsoft\\Edge\\User Data\\Default\\Web Applications";
private const string ResourcesDir = "Manifest Resources";
private const string IconsDir = "Icons";
private const string PwaDirIdentifier = "_CRX_";

private static List<PwaApp> edgePwaApps = new List<PwaApp>();
private static List<PwaApp> chromePwaApps = new List<PwaApp>();

public static int EdgeAppsCount { get => edgePwaApps.Count; }

public static int ChromeAppsCount { get => chromePwaApps.Count; }

public PwaHelper()
{
edgePwaApps = InitPwaData(EdgeBase);
chromePwaApps = InitPwaData(ChromeBase);
}

private List<PwaApp> InitPwaData(string p_baseDir)
{
List<PwaApp> result = new List<PwaApp>();
var baseFolderName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), p_baseDir);
if (Directory.Exists(baseFolderName))
{
foreach (string subDir in Directory.GetDirectories(baseFolderName))
{
string dirName = Path.GetFileName(subDir);
if (!dirName.StartsWith(PwaDirIdentifier, StringComparison.InvariantCultureIgnoreCase))
{
continue;
}

string appId = dirName.Substring(PwaDirIdentifier.Length, dirName.Length - PwaDirIdentifier.Length).Trim('_');

foreach (string iconFile in Directory.GetFiles(subDir, "*.ico"))
{
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(iconFile);

result.Add(new PwaApp() { Name = filenameWithoutExtension, IconFilename = iconFile, AppId = appId });
break;
}
}

string resourcesDir = Path.Combine(baseFolderName, ResourcesDir);
if (Directory.Exists(resourcesDir))
{
foreach (string subDir in Directory.GetDirectories(resourcesDir))
{
string dirName = Path.GetFileName(subDir);
if (result.Any(app => app.AppId == dirName))
{
continue;
}

string iconsDir = Path.Combine(subDir, IconsDir);
if (Directory.Exists(iconsDir))
{
foreach (string iconFile in Directory.GetFiles(iconsDir, "*.png"))
{
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(iconFile);

result.Add(new PwaApp() { Name = filenameWithoutExtension, IconFilename = iconFile, AppId = dirName });
break;
}
}
}
}
}

return result;
}

internal static string GetChromeAppIconFile(string pwaAppId)
{
var candidates = chromePwaApps.Where(x => x.AppId == pwaAppId).ToList();
if (candidates.Count > 0)
{
return candidates.First().IconFilename;
}

return null;
}

internal static string GetEdgeAppIconFile(string pwaAppId)
{
var candidates = edgePwaApps.Where(x => x.AppId == pwaAppId).ToList();
if (candidates.Count > 0)
{
return candidates.First().IconFilename;
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public void SerializeWorkspaces(List<Project> workspaces, bool useTempFile = fal
Title = app.AppTitle,
PackageFullName = app.PackageFullName,
AppUserModelId = app.AppUserModelId,
PwaAppId = app.PwaAppId,
CommandLineArguments = app.CommandLineArguments,
IsElevated = app.IsElevated,
CanLaunchElevated = app.CanLaunchElevated,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using System.Threading.Tasks;
using System.Timers;
using System.Windows;

using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
Expand All @@ -39,6 +38,7 @@ public class MainViewModel : INotifyPropertyChanged, IDisposable
private MainWindow _mainWindow;
private Timer lastUpdatedTimer;
private WorkspacesSettings settings;
private PwaHelper _pwaHelper;

public ObservableCollection<Project> Workspaces { get; set; } = new ObservableCollection<Project>();

Expand Down Expand Up @@ -147,6 +147,7 @@ public MainViewModel(WorkspacesEditorIO workspacesEditorIO)
settings = Utils.Settings.ReadSettings();
_orderByIndex = (int)settings.Properties.SortBy;
_workspacesEditorIO = workspacesEditorIO;
_pwaHelper = new PwaHelper();
lastUpdatedTimer = new System.Timers.Timer();
lastUpdatedTimer.Interval = 1000;
lastUpdatedTimer.Elapsed += LastUpdatedTimerElapsed;
Expand Down Expand Up @@ -601,5 +602,37 @@ private void SendDeleteTelemetryEvent()
telemetryEvent.Successful = true;
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
}

internal static bool IsPwaApp(Models.Application application)
{
if (application.IsEdge())
{
return true;
}
else if (application.IsChrome())
{
return true;
}
else
{
return false;
}
}

internal static string GetPwaIconFilename(Models.Application application)
{
if (application.IsEdge())
{
return PwaHelper.GetEdgeAppIconFile(application.PwaAppId);
}
else if (application.IsChrome())
{
return PwaHelper.GetChromeAppIconFile(application.PwaAppId);
}
else
{
return string.Empty;
}
}
SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved
SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
Margin="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding IconBitmapImage}" />
Source="{Binding IconBitmapImage, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock
Grid.Column="2"
Width="20"
Expand Down
Loading
Loading