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

Prevent Dev Home crashing when widgets don't supply icons or screenshots #3868

Merged
merged 3 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
using DevHome.Dashboard.ComSafeWidgetObjects;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;

namespace DevHome.Dashboard.Services;

public interface IWidgetIconService
{
public void RemoveIconsFromCache(string definitionId);

public Task<BitmapImage> GetIconFromCacheAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme theme);

public Task<Brush> GetBrushForWidgetIconAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme theme);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
using System.Threading.Tasks;
using DevHome.Dashboard.ComSafeWidgetObjects;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.UI.Xaml.Media;

namespace DevHome.Dashboard.Services;

public interface IWidgetScreenshotService
{
public void RemoveScreenshotsFromCache(string definitionId);

public Task<BitmapImage> GetScreenshotFromCacheAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme actualTheme);
public Task<Brush> GetBrushForWidgetScreenshotAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme theme);
}
17 changes: 15 additions & 2 deletions tools/Dashboard/DevHome.Dashboard/Services/WidgetIconService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void RemoveIconsFromCache(string definitionId)
_widgetDarkIconCache.TryRemove(definitionId, out _);
}

public async Task<BitmapImage> GetIconFromCacheAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme theme)
private async Task<BitmapImage> GetIconFromCacheAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme theme)
{
var widgetDefinitionId = widgetDefinition.Id;
BitmapImage bitmapImage;
Expand Down Expand Up @@ -75,11 +75,24 @@ public async Task<BitmapImage> GetIconFromCacheAsync(ComSafeWidgetDefinition wid

public async Task<Brush> GetBrushForWidgetIconAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme theme)
{
var image = await GetIconFromCacheAsync(widgetDefinition, theme);
var image = new BitmapImage();
try
{
image = await GetIconFromCacheAsync(widgetDefinition, theme);
}
catch (System.IO.FileNotFoundException fileNotFoundEx)
{
_log.Warning(fileNotFoundEx, $"Widget icon missing for widget definition {widgetDefinition.DisplayTitle}");
}
catch (Exception ex)
{
_log.Error(ex, $"Failed to get widget icon for widget definition {widgetDefinition.DisplayTitle}");
}

var brush = new ImageBrush
{
ImageSource = image,
Stretch = Stretch.Uniform,
};

return brush;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
using DevHome.Dashboard.ComSafeWidgetObjects;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.Windows.Widgets.Hosts;
using Serilog;
using Windows.Storage.Streams;

namespace DevHome.Dashboard.Services;

public class WidgetScreenshotService : IWidgetScreenshotService
{
private readonly ILogger _log = Log.ForContext("SourceContext", nameof(WidgetScreenshotService));

private readonly DispatcherQueue _dispatcherQueue;

private readonly ConcurrentDictionary<string, BitmapImage> _widgetLightScreenshotCache;
Expand All @@ -36,7 +40,7 @@ public void RemoveScreenshotsFromCache(string definitionId)
_widgetDarkScreenshotCache.Remove(definitionId, out _);
}

public async Task<BitmapImage> GetScreenshotFromCacheAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme actualTheme)
private async Task<BitmapImage> GetScreenshotFromCacheAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme actualTheme)
{
var widgetDefinitionId = widgetDefinition.Id;
BitmapImage bitmapImage;
Expand Down Expand Up @@ -71,6 +75,30 @@ public async Task<BitmapImage> GetScreenshotFromCacheAsync(ComSafeWidgetDefiniti
return bitmapImage;
}

public async Task<Brush> GetBrushForWidgetScreenshotAsync(ComSafeWidgetDefinition widgetDefinition, ElementTheme theme)
{
var image = new BitmapImage();
try
{
image = await GetScreenshotFromCacheAsync(widgetDefinition, theme);
}
catch (System.IO.FileNotFoundException fileNotFoundEx)
{
_log.Warning(fileNotFoundEx, $"Widget screenshot missing for widget definition {widgetDefinition.DisplayTitle}");
}
catch (Exception ex)
{
_log.Error(ex, $"Failed to get widget screenshot for widget definition {widgetDefinition.DisplayTitle}");
}

var brush = new ImageBrush
{
ImageSource = image,
};

return brush;
}

private async Task<BitmapImage> WidgetScreenshotToBitmapImageAsync(IRandomAccessStreamReference iconStreamRef)
{
// Return the bitmap image via TaskCompletionSource. Using WCT's EnqueueAsync does not suffice here, since if
Expand Down
12 changes: 2 additions & 10 deletions tools/Dashboard/DevHome.Dashboard/ViewModels/AddWidgetViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,10 @@ public AddWidgetViewModel(
public async Task SetWidgetDefinition(ComSafeWidgetDefinition selectedWidgetDefinition)
{
_selectedWidgetDefinition = selectedWidgetDefinition;
var bitmap = await _widgetScreenshotService.GetScreenshotFromCacheAsync(selectedWidgetDefinition, _themeSelectorService.GetActualTheme());

WidgetDisplayTitle = selectedWidgetDefinition.DisplayTitle;
WidgetProviderDisplayTitle = selectedWidgetDefinition.ProviderDefinitionDisplayName;
WidgetScreenshot = new ImageBrush
{
ImageSource = bitmap,
};
WidgetScreenshot = await _widgetScreenshotService.GetBrushForWidgetScreenshotAsync(selectedWidgetDefinition, _themeSelectorService.GetActualTheme());
PinButtonVisibility = true;
}

Expand All @@ -68,11 +64,7 @@ private async Task UpdateThemeAsync()
{
// Update the preview image for the selected widget.
var theme = _themeSelectorService.GetActualTheme();
var bitmap = await _widgetScreenshotService.GetScreenshotFromCacheAsync(_selectedWidgetDefinition, theme);
WidgetScreenshot = new ImageBrush
{
ImageSource = bitmap,
};
WidgetScreenshot = await _widgetScreenshotService.GetBrushForWidgetScreenshotAsync(_selectedWidgetDefinition, theme);
}
}
}
36 changes: 14 additions & 22 deletions tools/Dashboard/DevHome.Dashboard/Views/AddWidgetDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,10 @@ private async Task FillAvailableWidgetsAsync()

private async Task<Grid> BuildWidgetNavItem(ComSafeWidgetDefinition widgetDefinition)
{
var image = await _widgetIconService.GetIconFromCacheAsync(widgetDefinition, ActualTheme);
return BuildNavItem(image, widgetDefinition.DisplayTitle);
return await BuildNavItem(widgetDefinition);
}

private Grid BuildNavItem(BitmapImage image, string text)
private async Task<Grid> BuildNavItem(ComSafeWidgetDefinition widgetDefinition)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private async Task<Grid> BuildNavItem(ComSafeWidgetDefinition widgetDefinition)
private async Task<Grid> BuildNavItemAsync(ComSafeWidgetDefinition widgetDefinition)

{
var itemContent = new Grid
{
Expand All @@ -165,32 +164,26 @@ private Grid BuildNavItem(BitmapImage image, string text)
},
};

if (image is not null)
var imageBrush = await _widgetIconService.GetBrushForWidgetIconAsync(widgetDefinition, ActualTheme);

var itemSquare = new Rectangle()
{
var itemSquare = new Rectangle()
{
Width = 16,
Height = 16,
Margin = new Thickness(0, 0, 8, 0),
Fill = new ImageBrush
{
ImageSource = image,
Stretch = Stretch.Uniform,
},
};
Grid.SetColumn(itemSquare, 0);
Width = 16,
Height = 16,
Margin = new Thickness(0, 0, 8, 0),
Fill = imageBrush,
};

itemContent.Children.Add(itemSquare);
}
Grid.SetColumn(itemSquare, 0);
itemContent.Children.Add(itemSquare);

var itemText = new TextBlock()
{
Text = text,
Text = widgetDefinition.DisplayTitle,
TextWrapping = TextWrapping.Wrap,
VerticalAlignment = VerticalAlignment.Center,
};
Grid.SetColumn(itemText, 1);

itemContent.Children.Add(itemText);

return itemContent;
Expand Down Expand Up @@ -272,8 +265,7 @@ private async Task UpdateThemeAsync()
{
if (widgetItem.Tag is ComSafeWidgetDefinition widgetDefinition)
{
var image = await _widgetIconService.GetIconFromCacheAsync(widgetDefinition, ActualTheme);
widgetItem.Content = BuildNavItem(image, widgetDefinition.DisplayTitle);
widgetItem.Content = await BuildNavItem(widgetDefinition);
}
}
}
Expand Down
Loading