// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Services { /// /// Factory service for getting PowerToys module Settings that implement IHotkeyConfig /// public class SettingsFactory { private readonly SettingsUtils _settingsUtils; private readonly Dictionary _settingsTypes; public SettingsFactory(SettingsUtils settingsUtils) { _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); _settingsTypes = DiscoverSettingsTypes(); } /// /// Dynamically discovers all Settings types that implement IHotkeyConfig /// private Dictionary DiscoverSettingsTypes() { var settingsTypes = new Dictionary(); // Get the Settings.UI.Library assembly var assembly = Assembly.GetAssembly(typeof(IHotkeyConfig)); if (assembly == null) { return settingsTypes; } try { // Find all types that implement IHotkeyConfig and ISettingsConfig var hotkeyConfigTypes = assembly.GetTypes() .Where(type => type.IsClass && !type.IsAbstract && typeof(IHotkeyConfig).IsAssignableFrom(type) && typeof(ISettingsConfig).IsAssignableFrom(type)) .ToList(); foreach (var type in hotkeyConfigTypes) { // Try to get the ModuleName using SettingsRepository try { var repositoryType = typeof(SettingsRepository<>).MakeGenericType(type); var getInstanceMethod = repositoryType.GetMethod("GetInstance", BindingFlags.Public | BindingFlags.Static); var repository = getInstanceMethod?.Invoke(null, new object[] { _settingsUtils }); if (repository != null) { var settingsConfigProperty = repository.GetType().GetProperty("SettingsConfig"); var settingsInstance = settingsConfigProperty?.GetValue(repository) as ISettingsConfig; if (settingsInstance != null) { var moduleName = settingsInstance.GetModuleName(); if (string.IsNullOrEmpty(moduleName) && type == typeof(GeneralSettings)) { moduleName = "GeneralSettings"; } if (!string.IsNullOrEmpty(moduleName)) { settingsTypes[moduleName] = type; System.Diagnostics.Debug.WriteLine($"Discovered settings type: {type.Name} for module: {moduleName}"); } } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error getting module name for {type.Name}: {ex.Message}"); } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error scanning assembly {assembly.FullName}: {ex.Message}"); } return settingsTypes; } public IHotkeyConfig GetFreshSettings(string moduleKey) { if (!_settingsTypes.TryGetValue(moduleKey, out var settingsType)) { return null; } try { // Create a generic method call to _settingsUtils.GetSettingsOrDefault(moduleKey) var getSettingsMethod = typeof(SettingsUtils).GetMethod("GetSettingsOrDefault", new[] { typeof(string), typeof(string) }); var genericMethod = getSettingsMethod?.MakeGenericMethod(settingsType); // Call GetSettingsOrDefault(moduleKey) to get fresh settings from file string actualModuleKey = moduleKey; if (moduleKey == "GeneralSettings") { actualModuleKey = string.Empty; } var freshSettings = genericMethod?.Invoke(_settingsUtils, new object[] { actualModuleKey, "settings.json" }); return freshSettings as IHotkeyConfig; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error getting fresh settings for {moduleKey}: {ex.Message}"); return null; } } /// /// Gets a settings instance for the specified module using SettingsRepository /// /// The module key/name /// The settings instance implementing IHotkeyConfig, or null if not found public IHotkeyConfig GetSettings(string moduleKey) { if (!_settingsTypes.TryGetValue(moduleKey, out var settingsType)) { return null; } try { var repositoryType = typeof(SettingsRepository<>).MakeGenericType(settingsType); var getInstanceMethod = repositoryType.GetMethod("GetInstance", BindingFlags.Public | BindingFlags.Static); var repository = getInstanceMethod?.Invoke(null, new object[] { _settingsUtils }); if (repository != null) { var settingsConfigProperty = repository.GetType().GetProperty("SettingsConfig"); return settingsConfigProperty?.GetValue(repository) as IHotkeyConfig; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error getting Settings for {moduleKey}: {ex.Message}"); } return null; } /// /// Gets all available module names that have settings implementing IHotkeyConfig /// /// List of module names public List GetAvailableModuleNames() { return _settingsTypes.Keys.ToList(); } /// /// Gets all available settings that implement IHotkeyConfig /// /// Dictionary of module name to settings instance public Dictionary GetAllHotkeySettings() { var result = new Dictionary(); foreach (var moduleKey in _settingsTypes.Keys) { try { var settings = GetSettings(moduleKey); if (settings != null) { result[moduleKey] = settings; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error getting settings for {moduleKey}: {ex.Message}"); } } return result; } /// /// Gets a specific settings repository instance /// /// The settings type /// The settings repository instance public ISettingsRepository GetRepository() where T : class, ISettingsConfig, new() { return SettingsRepository.GetInstance(_settingsUtils); } } }