Skip to content
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
742 changes: 13 additions & 729 deletions src/ACAT.sln

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions src/Applications/ACATApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,9 @@ private static void InitializeDependencyInjection()

_serviceProvider = services.BuildServiceProvider();

// Make service provider available to Context for extension loading
Context.ServiceProvider = _serviceProvider;
// Resolve Context from DI – this sets Context.ServiceProvider automatically
// so all static Context.AppXXX accessors resolve managers through the container.
_serviceProvider.GetRequiredService<IContext>();

_logger.LogDebug("Dependency injection initialized with ACAT services");
}
Expand Down
5 changes: 3 additions & 2 deletions src/Applications/ACATTalk/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,9 @@ private static void InitializeDependencyInjection()

_serviceProvider = services.BuildServiceProvider();

// Make service provider available to Context for extension loading
Context.ServiceProvider = _serviceProvider;
// Resolve Context from DI – this sets Context.ServiceProvider automatically
// so all static Context.AppXXX accessors resolve managers through the container.
_serviceProvider.GetRequiredService<IContext>();

_logger.LogDebug("Dependency injection initialized with ACAT services");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,57 @@ public void ServiceProvider_CanBeSetToNull()
// Assert
Assert.IsNull(Context.ServiceProvider);
}

[TestMethod]
public void IContext_ResolvedFromServiceProvider_IsNotNull()
{
// Arrange
var services = new ServiceCollection();
services.AddLogging();
services.AddACATServices();
var serviceProvider = services.BuildServiceProvider();

// Act
var context = serviceProvider.GetService<IContext>();

// Assert
Assert.IsNotNull(context);
Assert.IsInstanceOfType(context, typeof(IContext));
}

[TestMethod]
public void IContext_ResolvingSetsStaticServiceProvider()
{
// Arrange
Context.ServiceProvider = null;
var services = new ServiceCollection();
services.AddLogging();
services.AddACATServices();
var serviceProvider = services.BuildServiceProvider();

// Act – resolving IContext should configure Context.ServiceProvider
serviceProvider.GetRequiredService<IContext>();

// Assert
Assert.IsNotNull(Context.ServiceProvider);
Assert.AreSame(serviceProvider, Context.ServiceProvider);
}

[TestMethod]
public void Context_AndIContext_ResolveSameSingletonInstance()
{
// Arrange
var services = new ServiceCollection();
services.AddLogging();
services.AddACATServices();
var serviceProvider = services.BuildServiceProvider();

// Act
var iContext = serviceProvider.GetRequiredService<IContext>();
var context = serviceProvider.GetRequiredService<Context>();

// Assert – both registrations return the same singleton
Assert.AreSame(iContext, context);
}
}
}
1 change: 1 addition & 0 deletions src/Libraries/ACATCore/ACAT.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@
<Compile Include="PanelManagement\PanelConfig\PanelConfigMap.cs" />
<Compile Include="PanelManagement\PanelConfig\PanelConfigMapEntry.cs" />
<Compile Include="PanelManagement\PanelManager.cs" />
<Compile Include="PanelManagement\IContext.cs" />
<Compile Include="PanelManagement\IPanelManager.cs" />
<Compile Include="PanelManagement\IPanelManagerFactory.cs" />
<Compile Include="PanelManagement\IScannerFactory.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,64 @@ namespace ACAT.Core.DependencyInjection
/// </remarks>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Registers the <see cref="Context"/> as a singleton and exposes it through
/// <see cref="IContext"/>. The factory sets <see cref="Context.ServiceProvider"/>
/// from the DI container so that all static <c>Context.AppXXX</c> accessor
/// properties resolve managers through the DI container automatically.
/// </summary>
public static IServiceCollection AddContextService(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));

services.AddSingleton<IContext>(provider =>
{
// The provider parameter might be a scope (ServiceProviderEngineScope).
// We need to ensure Context.ServiceProvider is set to the root provider
// so that static accessors always use the root, not a transient scope.
var rootProvider = GetRootProvider(provider);
return new Context(rootProvider);
});
services.AddSingleton<Context>(provider => (Context)provider.GetRequiredService<IContext>());
return services;
}

/// <summary>
/// Gets the root service provider from a potentially scoped provider.
/// </summary>
private static IServiceProvider GetRootProvider(IServiceProvider provider)
{
// Navigate through scopes to find the root provider
var current = provider;
var scopeType = Type.GetType("Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope, Microsoft.Extensions.DependencyInjection");

if (scopeType != null)
{
while (scopeType.IsInstanceOfType(current))
{
var rootProviderProperty = scopeType.GetProperty("RootProvider", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (rootProviderProperty != null)
{
var root = rootProviderProperty.GetValue(current) as IServiceProvider;
if (root != null && root != current)
{
current = root;
}
else
{
break;
}
}
else
{
break;
}
}
}

return current;
}

// ---------------------------------------------------------------
// Individual module registration methods
// ---------------------------------------------------------------
Expand Down Expand Up @@ -284,6 +342,7 @@ public static IServiceCollection AddACATCoreModules(this IServiceCollection serv
if (services == null) throw new ArgumentNullException(nameof(services));

services
.AddContextService()
.AddACATConfiguration()
.AddActuatorManagement()
.AddAgentManagement()
Expand All @@ -302,5 +361,16 @@ public static IServiceCollection AddACATCoreModules(this IServiceCollection serv

return services;
}

/// <summary>
/// Registers all ACAT services with the service container.
/// This is an alias for <see cref="AddACATCoreModules"/>.
/// </summary>
/// <param name="services">The service collection to configure.</param>
/// <returns>The service collection, for chaining.</returns>
public static IServiceCollection AddACATServices(this IServiceCollection services)
{
return AddACATCoreModules(services);
}
}
}
70 changes: 69 additions & 1 deletion src/Libraries/ACATCore/PanelManagement/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace ACAT.Core.PanelManagement
/// PostInit()
///
/// </summary>
public class Context
public class Context : IContext
{
private static readonly Lazy<AbbreviationsManager> _abbreviationsManager = new Lazy<AbbreviationsManager>(() => AbbreviationsManager.Instance);
private static readonly Lazy<ActuatorManager> _actuatorManager = new Lazy<ActuatorManager>(() => ActuatorManager.Instance);
Expand Down Expand Up @@ -82,6 +82,74 @@ static Context()
// This allows Context.ServiceProvider to be set before managers are created
}

/// <summary>
/// Initializes a new instance of Context for dependency injection.
/// Configures the static <see cref="ServiceProvider"/> from the injected
/// <paramref name="serviceProvider"/> so that all static accessor properties
/// resolve managers through the DI container.
/// </summary>
/// <param name="serviceProvider">The application DI service provider.</param>
public Context(IServiceProvider serviceProvider)
{
if (serviceProvider == null) throw new ArgumentNullException(nameof(serviceProvider));
ServiceProvider = serviceProvider;
}

// ---------------------------------------------------------------
// IContext instance implementation – each property / method
// delegates to the corresponding static member so that consumers
// using the injected IContext get the same behaviour as those
// using the static Context.AppXXX API.
// ---------------------------------------------------------------

AbbreviationsManager IContext.AppAbbreviationsManager => AppAbbreviationsManager;
ActuatorManager IContext.AppActuatorManager => AppActuatorManager;
AgentManager IContext.AppAgentMgr => AppAgentMgr;
AutomationEventManager IContext.AppAutomationEventManger => AppAutomationEventManger;
CommandManager IContext.AppCommandManager => AppCommandManager;
PanelManager IContext.AppPanelManager => AppPanelManager;

bool IContext.AppQuit
{
get => AppQuit;
set => AppQuit = value;
}

SpellCheckManager IContext.AppSpellCheckManager => AppSpellCheckManager;
ThemeManager IContext.AppThemeManager => AppThemeManager;
TTSManager IContext.AppTTSManager => AppTTSManager;

Windows.WindowPosition IContext.AppWindowPosition
{
get => AppWindowPosition;
set => AppWindowPosition = value;
}

WordPredictionManager IContext.AppWordPredictionManager => AppWordPredictionManager;
IEnumerable<String> IContext.ExtensionDirs => ExtensionDirs;

string IContext.KeyboardLayout
{
get => KeyboardLayout;
set => KeyboardLayout = value;
}

bool IContext.RestartKeyboardLayout
{
get => RestartKeyboardLayout;
set => RestartKeyboardLayout = value;
}

bool IContext.ShowTalkWindowOnStartup
{
get => ShowTalkWindowOnStartup;
set => ShowTalkWindowOnStartup = value;
}

TInterface IContext.GetManager<TInterface>() => GetManager<TInterface>();
string IContext.GetInitCompletionStatus() => GetInitCompletionStatus();
bool IContext.IsInitFatal() => IsInitFatal();

/// <summary>
/// Raised when the culture changes
/// </summary>
Expand Down
92 changes: 92 additions & 0 deletions src/Libraries/ACATCore/PanelManagement/IContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013-2019; 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
////////////////////////////////////////////////////////////////////////////

using ACAT.Core.AbbreviationsManagement;
using ACAT.Core.ActuatorManagement;
using ACAT.Core.AgentManagement;
using ACAT.Core.CommandManagement;
using ACAT.Core.SpellCheckManagement;
using ACAT.Core.ThemeManagement;
using ACAT.Core.TTSManagement;
using ACAT.Core.Utility;
using ACAT.Core.WordPredictorManagement;
using System;
using System.Collections.Generic;

namespace ACAT.Core.PanelManagement
{
/// <summary>
/// Provides access to ACAT system-wide managers and configuration.
/// Implement this interface when consuming Context via dependency injection;
/// it mirrors the static <see cref="Context"/> API so that callers can
/// switch from <c>Context.AppXXX</c> to an injected <see cref="IContext"/>
/// without changing the property names.
/// </summary>
public interface IContext
{
/// <summary>Gets the Abbreviations Manager.</summary>
AbbreviationsManager AppAbbreviationsManager { get; }

/// <summary>Gets the Actuator Manager.</summary>
ActuatorManager AppActuatorManager { get; }

/// <summary>Gets the Agent Manager.</summary>
AgentManager AppAgentMgr { get; }

/// <summary>Gets the Automation Event Manager.</summary>
AutomationEventManager AppAutomationEventManger { get; }

/// <summary>Gets the Command Manager.</summary>
CommandManager AppCommandManager { get; }

/// <summary>Gets the Panel Manager.</summary>
PanelManager AppPanelManager { get; }

/// <summary>Gets or sets whether the application should quit.</summary>
bool AppQuit { get; set; }

/// <summary>Gets the Spell Check Manager.</summary>
SpellCheckManager AppSpellCheckManager { get; }

/// <summary>Gets the Theme Manager.</summary>
ThemeManager AppThemeManager { get; }

/// <summary>Gets the Text-to-Speech Manager.</summary>
TTSManager AppTTSManager { get; }

/// <summary>Gets or sets the current scanner window position.</summary>
Windows.WindowPosition AppWindowPosition { get; set; }

/// <summary>Gets the Word Prediction Manager.</summary>
WordPredictionManager AppWordPredictionManager { get; }

/// <summary>Gets the list of extension directories.</summary>
IEnumerable<String> ExtensionDirs { get; }

/// <summary>Gets or sets the keyboard layout command name.</summary>
string KeyboardLayout { get; set; }

/// <summary>Gets or sets whether a keyboard-layout change was requested.</summary>
bool RestartKeyboardLayout { get; set; }

/// <summary>Gets or sets whether the talk window should be shown on startup.</summary>
bool ShowTalkWindowOnStartup { get; set; }

/// <summary>
/// Resolves a manager by interface type from the service provider.
/// </summary>
/// <typeparam name="TInterface">The interface type to resolve.</typeparam>
/// <returns>The manager instance, or <c>null</c> if not registered.</returns>
TInterface GetManager<TInterface>() where TInterface : class;

/// <summary>Returns the initialization completion status string.</summary>
string GetInitCompletionStatus();

/// <summary>Returns whether the initialization error was fatal.</summary>
bool IsInitFatal();
}
}
5 changes: 5 additions & 0 deletions src/Libraries/ACATCore/WidgetManagement/WidgetManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ public static Type GetWidgetType(String widgetTypeName)
/// <returns>Full type name, empty string if invalid</returns>
public static String GetWidgetTypeFullName(String widgetTypeName)
{
if (_widgetTypeCollection == null)
{
return String.Empty;
}

foreach (String s in _widgetTypeCollection)
{
if (s.EndsWith("." + widgetTypeName, StringComparison.InvariantCultureIgnoreCase))
Expand Down
Loading