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

Pin default widgets #2233

Merged
merged 2 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions common/Helpers/WellKnownSettingsKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ namespace DevHome.Common.Helpers;
public class WellKnownSettingsKeys
{
public const string IsNotFirstRun = "IsNotFirstRun";

public const string IsNotFirstDashboardRun = "IsNotFirstDashboardRun";
}
17 changes: 17 additions & 0 deletions tools/Dashboard/DevHome.Dashboard/Helpers/WidgetHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,23 @@ internal sealed class WidgetHelpers
public const string WidgetServiceStorePackageId = "9N3RK8ZV2ZR8";
public const string WidgetServicePackageFamilyName = "Microsoft.WidgetsPlatformRuntime_8wekyb3d8bbwe";

public static readonly string[] DefaultWidgetDefinitionIds =
{
#if CANARY_BUILD
"Microsoft.Windows.DevHome.Canary_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_CPUUsage",
"Microsoft.Windows.DevHome.Canary_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_GPUUsage",
"Microsoft.Windows.DevHome.Canary_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_NetworkUsage",
#elif STABLE_BUILD
"Microsoft.Windows.DevHome_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_CPUUsage",
"Microsoft.Windows.DevHome_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_GPUUsage",
"Microsoft.Windows.DevHome_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_NetworkUsage",
#else
"Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_CPUUsage",
"Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_GPUUsage",
"Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe!App!!CoreWidgetProvider!!System_NetworkUsage",
#endif
};

public const string DevHomeHostName = "DevHome";

private const double WidgetPxHeightSmall = 146;
Expand Down
89 changes: 84 additions & 5 deletions tools/Dashboard/DevHome.Dashboard/Views/DashboardView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Input;
using DevHome.Common;
using DevHome.Common.Contracts;
using DevHome.Common.Extensions;
using DevHome.Common.Helpers;
using DevHome.Dashboard.Controls;
using DevHome.Dashboard.Helpers;
using DevHome.Dashboard.Services;
Expand All @@ -21,6 +23,7 @@
using Microsoft.Windows.Widgets;
using Microsoft.Windows.Widgets.Hosts;
using Windows.System;
using Log = DevHome.Dashboard.Helpers.Log;

namespace DevHome.Dashboard.Views;

Expand All @@ -37,6 +40,7 @@ public partial class DashboardView : ToolPage
public static ObservableCollection<WidgetViewModel> PinnedWidgets { get; set; }

private static Microsoft.UI.Dispatching.DispatcherQueue _dispatcher;
private readonly ILocalSettingsService _localSettingsService;

private const string DraggedWidget = "DraggedWidget";
private const string DraggedIndex = "DraggedIndex";
Expand All @@ -52,6 +56,7 @@ public DashboardView()
PinnedWidgets = new ObservableCollection<WidgetViewModel>();

_dispatcher = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
_localSettingsService = Application.Current.GetService<ILocalSettingsService>();

ActualThemeChanged += OnActualThemeChanged;

Expand Down Expand Up @@ -119,7 +124,13 @@ private async Task InitializeDashboard()
// Cache the widget icons before we display the widgets, since we include the icons in the widgets.
await ViewModel.WidgetIconService.CacheAllWidgetIconsAsync();

await RestorePinnedWidgetsAsync();
var isFirstDashboardRun = !(await _localSettingsService.ReadSettingAsync<bool>(WellKnownSettingsKeys.IsNotFirstDashboardRun));
if (isFirstDashboardRun)
{
await Application.Current.GetService<ILocalSettingsService>().SaveSettingAsync(WellKnownSettingsKeys.IsNotFirstDashboardRun, true);
}

await InitializePinnedWidgetListAsync(isFirstDashboardRun);
}
else
{
Expand All @@ -141,7 +152,22 @@ private async Task InitializeDashboard()
ViewModel.IsLoading = false;
}

private async Task RestorePinnedWidgetsAsync()
private async Task InitializePinnedWidgetListAsync(bool isFirstDashboardRun)
{
var hostWidgets = await GetPreviouslyPinnedWidgets();

if ((hostWidgets == null || hostWidgets.Length == 0) && isFirstDashboardRun)
{
// If it's the first time the Dashboard has been displayed and we have no other widgets pinned to a
// different version of Dev Home, pin some default widgets.
await PinDefaultWidgetsAsync();
return;
}

await RestorePinnedWidgetsAsync(hostWidgets);
}

private async Task<Widget[]> GetPreviouslyPinnedWidgets()
{
Log.Logger()?.ReportInfo("DashboardView", "Get widgets for current host");
var widgetHost = await ViewModel.WidgetHostingService.GetWidgetHostAsync();
Expand All @@ -150,10 +176,16 @@ private async Task RestorePinnedWidgetsAsync()
if (hostWidgets == null)
{
Log.Logger()?.ReportInfo("DashboardView", $"Found 0 widgets for this host");
return;
return null;
}

Log.Logger()?.ReportInfo("DashboardView", $"Found {hostWidgets.Length} widgets for this host");

return hostWidgets;
}

private async Task RestorePinnedWidgetsAsync(Widget[] hostWidgets)
{
var restoredWidgetsWithPosition = new SortedDictionary<int, Widget>();
var restoredWidgetsWithoutPosition = new SortedDictionary<int, Widget>();
var numUnorderedWidgets = 0;
Expand All @@ -172,7 +204,7 @@ private async Task RestorePinnedWidgetsAsync()
{
// If we have a widget with no state, Dev Home does not consider it a valid widget
// and should delete it, rather than letting it run invisibly in the background.
await DeleteAbandonedWidgetAsync(widget, widgetHost);
await DeleteAbandonedWidgetAsync(widget);
continue;
}

Expand Down Expand Up @@ -219,8 +251,10 @@ private async Task RestorePinnedWidgetsAsync()
}
}

private async Task DeleteAbandonedWidgetAsync(Widget widget, WidgetHost widgetHost)
private async Task DeleteAbandonedWidgetAsync(Widget widget)
{
var widgetHost = await ViewModel.WidgetHostingService.GetWidgetHostAsync();

var length = await Task.Run(() => widgetHost!.GetWidgets().Length);
Log.Logger()?.ReportInfo("DashboardView", $"Found abandoned widget, try to delete it...");
Log.Logger()?.ReportInfo("DashboardView", $"Before delete, {length} widgets for this host");
Expand All @@ -240,6 +274,51 @@ private async Task PlaceWidget(KeyValuePair<int, Widget> orderedWidget, int fina
await WidgetHelpers.SetPositionCustomStateAsync(widget, finalPlace);
}

private async Task PinDefaultWidgetsAsync()
{
var catalog = await ViewModel.WidgetHostingService.GetWidgetCatalogAsync();

if (catalog is null)
{
Log.Logger()?.ReportError("AddWidgetDialog", $"Trying to pin default widgets, but WidgetCatalog is null.");
return;
}

var widgetDefinitions = await Task.Run(() => catalog!.GetWidgetDefinitions().OrderBy(x => x.DisplayTitle));
foreach (var widgetDefinition in widgetDefinitions)
{
var id = widgetDefinition.Id;
if (WidgetHelpers.DefaultWidgetDefinitionIds.Contains(id))
{
await PinDefaultWidgetAsync(widgetDefinition);
}
}
}

private async Task PinDefaultWidgetAsync(WidgetDefinition defaultWidgetDefinition)
{
try
{
// Create widget
var widgetHost = await ViewModel.WidgetHostingService.GetWidgetHostAsync();
var size = WidgetHelpers.GetDefaultWidgetSize(defaultWidgetDefinition.GetWidgetCapabilities());
var newWidget = await Task.Run(async () => await widgetHost?.CreateWidgetAsync(defaultWidgetDefinition.Id, size));

// Set custom state on new widget.
var position = PinnedWidgets.Count;
var newCustomState = WidgetHelpers.CreateWidgetCustomState(position);
Log.Logger()?.ReportDebug("DashboardView", $"SetCustomState: {newCustomState}");
await newWidget.SetCustomStateAsync(newCustomState);

// Put new widget on the Dashboard.
await InsertWidgetInPinnedWidgetsAsync(newWidget, size, position);
}
catch (Exception ex)
{
Log.Logger()?.ReportError("AddWidgetDialog", $"PinDefaultWidget failed: ", ex);
}
}

[RelayCommand]
public async Task GoToWidgetsInStoreAsync()
{
Expand Down
Loading