-
Notifications
You must be signed in to change notification settings - Fork 358
/
Copy pathAdaptiveCardRenderingService.cs
149 lines (123 loc) · 5.16 KB
/
AdaptiveCardRenderingService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Threading;
using System.Threading.Tasks;
using AdaptiveCards.Rendering.WinUI3;
using DevHome.Common.Renderers;
using DevHome.Contracts.Services;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Serilog;
using Windows.Storage;
namespace DevHome.Common.Services;
public class AdaptiveCardRenderingService : IAdaptiveCardRenderingService, IDisposable
{
private readonly ILogger _log = Log.ForContext("SourceContext", nameof(AdaptiveCardRenderingService));
public event EventHandler RendererUpdated = (_, _) => { };
private readonly DispatcherQueue _dispatcherQueue;
private readonly IThemeSelectorService _themeSelectorService;
private readonly SemaphoreSlim _rendererLock = new(1, 1);
private AdaptiveCardRenderer? _renderer;
private bool _disposedValue;
public AdaptiveCardRenderingService(DispatcherQueue dispatcherQueue, IThemeSelectorService themeSelectorService)
{
_dispatcherQueue = dispatcherQueue;
_themeSelectorService = themeSelectorService;
_themeSelectorService.ThemeChanged += OnThemeChanged;
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_rendererLock.Dispose();
}
_disposedValue = true;
}
}
public async Task<AdaptiveCardRenderer> GetRendererAsync()
{
// We need to lock the renderer, otherwise another widget could come in after the renderer
// is created but before it is configured and render the widget without configuration.
await _rendererLock.WaitAsync();
try
{
if (_renderer == null)
{
_renderer = new AdaptiveCardRenderer();
await ConfigureAdaptiveCardRendererAsync();
}
return _renderer;
}
finally
{
_rendererLock.Release();
}
}
private async Task ConfigureAdaptiveCardRendererAsync()
{
if (_renderer == null)
{
return;
}
// Add custom Adaptive Card renderer.
_renderer.ElementRenderers.Set(LabelGroup.CustomTypeString, new LabelGroupRenderer());
_renderer.ElementRenderers.Set("Input.ChoiceSet", new AccessibleChoiceSet());
_renderer.ElementRenderers.Set("Input.Text", new TextInputRenderer());
_renderer.ActionRenderers.Set(ChooseFileAction.CustomTypeString, new ChooseFileActionRenderer());
// A different host config is used to render widgets (adaptive cards) in light and dark themes.
await UpdateHostConfig();
// Due to a bug in the Adaptive Card renderer (https://github.com/microsoft/AdaptiveCards/issues/8840)
// positive and destructive buttons render with square corners. Override with XAML styles.
var positiveStyle = Application.Current.Resources["AccentButtonStyle"] as Style;
var destructiveStyle = Application.Current.Resources["DefaultButtonStyle"] as Style;
_renderer.OverrideStyles = new ResourceDictionary
{
{ "Adaptive.Action.Positive", positiveStyle },
{ "Adaptive.Action.Destructive", destructiveStyle },
};
}
private async Task UpdateHostConfig()
{
if (_renderer != null)
{
// Add host config for current theme.
var hostConfigContents = string.Empty;
var hostConfigFileName = _themeSelectorService.IsDarkTheme() ? "DarkHostConfig.json" : "LightHostConfig.json";
try
{
_log.Error($"Get HostConfig file '{hostConfigFileName}'");
var uri = new Uri($"ms-appx:///DevHome.Settings/Assets/{hostConfigFileName}");
var file = await StorageFile.GetFileFromApplicationUriAsync(uri).AsTask().ConfigureAwait(false);
hostConfigContents = await FileIO.ReadTextAsync(file);
}
catch (Exception ex)
{
_log.Error(ex, "Error retrieving HostConfig");
}
_dispatcherQueue.TryEnqueue(() =>
{
if (!string.IsNullOrEmpty(hostConfigContents))
{
_renderer.HostConfig = AdaptiveHostConfig.FromJsonString(hostConfigContents).HostConfig;
// Remove margins from selectAction.
_renderer.AddSelectActionMargin = false;
_renderer.HostConfig.ContainerStyles.Default.BackgroundColor = Microsoft.UI.Colors.Transparent;
}
else
{
_log.Error($"HostConfig contents are {hostConfigContents}");
}
});
RendererUpdated(this, new EventArgs());
}
}
private async void OnThemeChanged(object? sender, ElementTheme e) => await UpdateHostConfig();
}