-
Notifications
You must be signed in to change notification settings - Fork 0
Add cross-platform support via AdaptiveRemote.Core project #24
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
Changes from 6 commits
6de1595
870fabd
2b95173
49d49cf
8c42c29
1edf56f
b7f127d
86c391a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net8.0</TargetFramework> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <RootNamespace>AdaptiveRemote</RootNamespace> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <None Remove="Services\Conversation\static_grammar.xml" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <EmbeddedResource Include="Services\Conversation\static_grammar.xml" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.2" /> | ||
| <PackageReference Include="Azure.Identity" Version="1.11.4" /> | ||
| <PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.2.0" /> | ||
| <PackageReference Include="I8Beef.TiVo" Version="1.0.0.14" /> | ||
| <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.14" /> | ||
| <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> | ||
| <PackageReference Include="Nerdbank.GitVersioning" Version="3.9.50"> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.8.1" /> | ||
| <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" /> | ||
| <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" /> | ||
| <PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" /> | ||
| <PackageReference Include="System.Text.Json" Version="8.0.5" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Update="Logging\LoggingMessages.Designer.cs"> | ||
| <DesignTime>True</DesignTime> | ||
| <AutoGen>True</AutoGen> | ||
| <DependentUpon>LoggingMessages.resx</DependentUpon> | ||
| </Compile> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <EmbeddedResource Update="Logging\LoggingMessages.resx"> | ||
| <Generator>ResXFileCodeGenerator</Generator> | ||
| <LastGenOutput>LoggingMessages.Designer.cs</LastGenOutput> | ||
| </EmbeddedResource> | ||
| </ItemGroup> | ||
|
|
||
| </Project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,6 @@ | |
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Hosting; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
||
| namespace AdaptiveRemote.Configuration; | ||
|
|
||
|
|
@@ -17,36 +16,18 @@ internal static IServiceCollection AddConversationServices(this IServiceCollecti | |
| .AddScopedLifecycleService<ConversationController>() | ||
| .AddScoped<ISpeechRecognition, SpeechRecognition>() | ||
| .AddScoped<ISpeechSynthesis, SpeechSynthesis>() | ||
| .AddScoped<IGrammarProvider, StaticGrammarProvider>() | ||
| .AddScoped<ConversationStateMachine>() | ||
| .AddSingleton<ISpeechSynthesizer, SpeechSynthesizerWrapper>() | ||
| .AddSingleton<ISpeechRecognitionEngine, SpeechRecognitionEngineWrapper>() | ||
| .AddSingleton<IAudioConfigurationService, DefaultDeviceAudioConfiguration>() | ||
| .AddSingleton<IListeningController, ListeningController>() | ||
| .AddScoped(GetConversationViewModel); | ||
|
|
||
| internal static IServiceCollection AddConversationServices(this IServiceCollection services, IConfiguration config) | ||
| => services | ||
| .AddConversationServices() | ||
| .OptionallyAddFakeSpeechRecognition(config) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OptionallyAddFakeSpeechRecognition and OptionallyAddSamplesRecorder shouldn't be removed. They're not related to the "Fakes" that were added for Electron. Rather, this is simulated speech and diagnostics that are used for testing without having to speak commands out loud.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 86c391a - restored FakeSpeechRecognitionEngine and SamplesRecorder support. The Windows project now registers |
||
| .OptionallyAddSamplesRecorder(config) | ||
| .Configure<ConversationSettings>(config); | ||
|
|
||
| private static Models.ConversationView GetConversationViewModel(IServiceProvider provider) | ||
| { | ||
| IRemoteDefinitionService definition = provider.GetRequiredService<IRemoteDefinitionService>(); | ||
| return definition.GetElement<Models.ConversationView>(); | ||
| } | ||
|
|
||
| private static IServiceCollection OptionallyAddFakeSpeechRecognition(this IServiceCollection services, IConfiguration config) | ||
| => config.GetValue<bool>(nameof(ConversationSettings.Fake)) == true | ||
| ? services.AddSingleton<ISpeechRecognitionEngine, FakeSpeechRecognitionEngine>() | ||
| : services; | ||
|
|
||
| private static IServiceCollection OptionallyAddSamplesRecorder(this IServiceCollection services, IConfiguration config) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to self: Find out where this went |
||
| => config.GetValue<bool>(nameof(ConversationSettings.RecordSamples)) == false | ||
| ? services | ||
| : services | ||
| .AddHostedService<SamplesRecorder>() | ||
| .AddSingleton<ILoggerProvider, SamplesRecorder.LoggerProvider>(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| using AdaptiveRemote.Models; | ||
| using AdaptiveRemote.Services; | ||
| using AdaptiveRemote.Services.Lifecycle; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Hosting; | ||
|
|
||
| namespace AdaptiveRemote.Configuration; | ||
|
|
||
| /// <summary> | ||
| /// Public extension methods for configuring the Electron host | ||
| /// </summary> | ||
| public static class ElectronHostBuilderExtensions | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should have one configuration path, not two different ones. What I would like to see is this:
In general, I want there to be as little code as possible in the Windows and Electron projects. Only things with Windows dependencies belong in the Windows project, and their analogs go in the Electron project. There should be no "Electron-specific" or "Windows-specific" code in the Core project. Abstractions in the Core project should be defined so they are host-agnostic and can be used exactly the same by both hosts. The goal is that the Electron app can be run in a Linux runner and we can be reasonably sure that the Windows version will run almost exactly the same way.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 86c391a - unified configuration via |
||
| { | ||
| /// <summary> | ||
| /// Configures the application for Electron hosting with fake speech services | ||
| /// </summary> | ||
| public static IHostBuilder ConfigureElectronApp(this IHostBuilder hostBuilder) | ||
| => hostBuilder | ||
| .ConfigureTelemetry() | ||
| .AddRemoteServices() | ||
| .AddBroadlinkSupport() | ||
| .AddTiVoSupport() | ||
| .AddConversationSystem() | ||
| .AddSystemWrapperServices(); | ||
|
|
||
| /// <summary> | ||
| /// Adds the lifecycle view model and controller for Electron hosting | ||
| /// </summary> | ||
| public static IServiceCollection AddElectronLifecycle(this IServiceCollection services) | ||
| { | ||
| var lifecycleView = new LifecycleView(); | ||
| var lifecycleController = new LifecycleViewController(lifecycleView); | ||
| services.AddSingleton(lifecycleView); | ||
| services.AddSingleton<ILifecycleViewController>(lifecycleController); | ||
| return services; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Adds the scope factory for Electron hosting | ||
| /// </summary> | ||
| public static IServiceCollection AddElectronScopeFactory(this IServiceCollection services) | ||
| => services.AddSingleton<IApplicationScopeFactory, ElectronScopeFactory>(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| namespace AdaptiveRemote.Services.Conversation; | ||
|
|
||
| /// <summary> | ||
| /// Platform-independent grammar interface that abstracts System.Speech.Recognition.Grammar | ||
| /// </summary> | ||
| public interface IGrammar | ||
| { | ||
| /// <summary> | ||
| /// Gets or sets the name of the grammar. | ||
| /// </summary> | ||
| string Name { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets whether the grammar is enabled for recognition. | ||
| /// </summary> | ||
| bool Enabled { get; set; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| namespace AdaptiveRemote.Services.Conversation; | ||
|
|
||
| /// <summary> | ||
| /// Loader for Grammar objects | ||
| /// </summary> | ||
| public interface IGrammarProvider | ||
| { | ||
| /// <summary> | ||
| /// Load a grammar that supports the given kind of phrases | ||
| /// </summary> | ||
| IGrammar LoadGrammar(PhraseKinds phraseKind); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| using Microsoft.Extensions.DependencyInjection; | ||
|
|
||
| namespace AdaptiveRemote.Services.Lifecycle; | ||
|
|
||
| /// <summary> | ||
| /// IApplicationScopeFactory implementation for Electron/Blazor Server hosting | ||
| /// </summary> | ||
| internal class ElectronScopeFactory : IApplicationScopeFactory, IApplicationScope | ||
| { | ||
| private readonly IServiceProvider _serviceProvider; | ||
| private IServiceScope? _scope; | ||
|
|
||
| public ElectronScopeFactory(IServiceProvider serviceProvider) | ||
| { | ||
| _serviceProvider = serviceProvider; | ||
| } | ||
|
|
||
| public Task<IApplicationScope> CreateNewScopeAsync(CancellationToken cancellationToken) | ||
| { | ||
| _scope?.Dispose(); | ||
| _scope = _serviceProvider.CreateScope(); | ||
| return Task.FromResult<IApplicationScope>(this); | ||
| } | ||
|
|
||
| public async Task TryInvokeAsync(Func<IServiceProvider, CancellationToken, Task> workItem, CancellationToken cancellationToken) | ||
| { | ||
| var scopeServices = _scope?.ServiceProvider ?? _serviceProvider; | ||
| await workItem(scopeServices, cancellationToken); | ||
| } | ||
|
|
||
| public void Dispose() | ||
| { | ||
| _scope?.Dispose(); | ||
| _scope = null; | ||
| GC.SuppressFinalize(this); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
jodavis marked this conversation as resolved.
|
||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net8.0</TargetFramework> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <RootNamespace>AdaptiveRemote</RootNamespace> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="ElectronNET.API" Version="23.6.2" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\AdaptiveRemote.Core\AdaptiveRemote.Core.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that the root namespace is still
AdaptiveRemoteand notAdaptiveRemote.Core. However, I think the generated Blazor component code is going to useAdaptiveRemote.Corefor its namespaces. If it's possible, I want that to be configured asAdaptiveRemotenamespace as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already configured correctly - the Core csproj has
<RootNamespace>AdaptiveRemote</RootNamespace>which means Blazor components inComponents/folder are generated with namespaceAdaptiveRemote.Components, notAdaptiveRemote.Core.Components.