/ src / settings-ui / Settings.UI.Library / SettingsFactory.cs
SettingsFactory.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  using System;
  6  using System.Collections.Generic;
  7  using System.Linq;
  8  using System.Reflection;
  9  using Microsoft.PowerToys.Settings.UI.Library;
 10  using Microsoft.PowerToys.Settings.UI.Library.Helpers;
 11  using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
 12  
 13  namespace Microsoft.PowerToys.Settings.UI.Services
 14  {
 15      /// <summary>
 16      /// Factory service for getting PowerToys module Settings that implement IHotkeyConfig
 17      /// </summary>
 18      public class SettingsFactory
 19      {
 20          private readonly SettingsUtils _settingsUtils;
 21          private readonly Dictionary<string, Type> _settingsTypes;
 22  
 23          public SettingsFactory(SettingsUtils settingsUtils)
 24          {
 25              _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
 26              _settingsTypes = DiscoverSettingsTypes();
 27          }
 28  
 29          /// <summary>
 30          /// Dynamically discovers all Settings types that implement IHotkeyConfig
 31          /// </summary>
 32          private Dictionary<string, Type> DiscoverSettingsTypes()
 33          {
 34              var settingsTypes = new Dictionary<string, Type>();
 35  
 36              // Get the Settings.UI.Library assembly
 37              var assembly = Assembly.GetAssembly(typeof(IHotkeyConfig));
 38              if (assembly == null)
 39              {
 40                  return settingsTypes;
 41              }
 42  
 43              try
 44              {
 45                  // Find all types that implement IHotkeyConfig and ISettingsConfig
 46                  var hotkeyConfigTypes = assembly.GetTypes()
 47                      .Where(type =>
 48                          type.IsClass &&
 49                          !type.IsAbstract &&
 50                          typeof(IHotkeyConfig).IsAssignableFrom(type) &&
 51                          typeof(ISettingsConfig).IsAssignableFrom(type))
 52                      .ToList();
 53  
 54                  foreach (var type in hotkeyConfigTypes)
 55                  {
 56                      // Try to get the ModuleName using SettingsRepository
 57                      try
 58                      {
 59                          var repositoryType = typeof(SettingsRepository<>).MakeGenericType(type);
 60                          var getInstanceMethod = repositoryType.GetMethod("GetInstance", BindingFlags.Public | BindingFlags.Static);
 61                          var repository = getInstanceMethod?.Invoke(null, new object[] { _settingsUtils });
 62  
 63                          if (repository != null)
 64                          {
 65                              var settingsConfigProperty = repository.GetType().GetProperty("SettingsConfig");
 66                              var settingsInstance = settingsConfigProperty?.GetValue(repository) as ISettingsConfig;
 67  
 68                              if (settingsInstance != null)
 69                              {
 70                                  var moduleName = settingsInstance.GetModuleName();
 71                                  if (string.IsNullOrEmpty(moduleName) && type == typeof(GeneralSettings))
 72                                  {
 73                                      moduleName = "GeneralSettings";
 74                                  }
 75  
 76                                  if (!string.IsNullOrEmpty(moduleName))
 77                                  {
 78                                      settingsTypes[moduleName] = type;
 79                                      System.Diagnostics.Debug.WriteLine($"Discovered settings type: {type.Name} for module: {moduleName}");
 80                                  }
 81                              }
 82                          }
 83                      }
 84                      catch (Exception ex)
 85                      {
 86                          System.Diagnostics.Debug.WriteLine($"Error getting module name for {type.Name}: {ex.Message}");
 87                      }
 88                  }
 89              }
 90              catch (Exception ex)
 91              {
 92                  System.Diagnostics.Debug.WriteLine($"Error scanning assembly {assembly.FullName}: {ex.Message}");
 93              }
 94  
 95              return settingsTypes;
 96          }
 97  
 98          public IHotkeyConfig GetFreshSettings(string moduleKey)
 99          {
100              if (!_settingsTypes.TryGetValue(moduleKey, out var settingsType))
101              {
102                  return null;
103              }
104  
105              try
106              {
107                  // Create a generic method call to _settingsUtils.GetSettingsOrDefault<T>(moduleKey)
108                  var getSettingsMethod = typeof(SettingsUtils).GetMethod("GetSettingsOrDefault", new[] { typeof(string), typeof(string) });
109                  var genericMethod = getSettingsMethod?.MakeGenericMethod(settingsType);
110  
111                  // Call GetSettingsOrDefault<T>(moduleKey) to get fresh settings from file
112                  string actualModuleKey = moduleKey;
113                  if (moduleKey == "GeneralSettings")
114                  {
115                      actualModuleKey = string.Empty;
116                  }
117  
118                  var freshSettings = genericMethod?.Invoke(_settingsUtils, new object[] { actualModuleKey, "settings.json" });
119  
120                  return freshSettings as IHotkeyConfig;
121              }
122              catch (Exception ex)
123              {
124                  System.Diagnostics.Debug.WriteLine($"Error getting fresh settings for {moduleKey}: {ex.Message}");
125                  return null;
126              }
127          }
128  
129          /// <summary>
130          /// Gets a settings instance for the specified module using SettingsRepository
131          /// </summary>
132          /// <param name="moduleKey">The module key/name</param>
133          /// <returns>The settings instance implementing IHotkeyConfig, or null if not found</returns>
134          public IHotkeyConfig GetSettings(string moduleKey)
135          {
136              if (!_settingsTypes.TryGetValue(moduleKey, out var settingsType))
137              {
138                  return null;
139              }
140  
141              try
142              {
143                  var repositoryType = typeof(SettingsRepository<>).MakeGenericType(settingsType);
144                  var getInstanceMethod = repositoryType.GetMethod("GetInstance", BindingFlags.Public | BindingFlags.Static);
145                  var repository = getInstanceMethod?.Invoke(null, new object[] { _settingsUtils });
146  
147                  if (repository != null)
148                  {
149                      var settingsConfigProperty = repository.GetType().GetProperty("SettingsConfig");
150                      return settingsConfigProperty?.GetValue(repository) as IHotkeyConfig;
151                  }
152              }
153              catch (Exception ex)
154              {
155                  System.Diagnostics.Debug.WriteLine($"Error getting Settings for {moduleKey}: {ex.Message}");
156              }
157  
158              return null;
159          }
160  
161          /// <summary>
162          /// Gets all available module names that have settings implementing IHotkeyConfig
163          /// </summary>
164          /// <returns>List of module names</returns>
165          public List<string> GetAvailableModuleNames()
166          {
167              return _settingsTypes.Keys.ToList();
168          }
169  
170          /// <summary>
171          /// Gets all available settings that implement IHotkeyConfig
172          /// </summary>
173          /// <returns>Dictionary of module name to settings instance</returns>
174          public Dictionary<string, IHotkeyConfig> GetAllHotkeySettings()
175          {
176              var result = new Dictionary<string, IHotkeyConfig>();
177  
178              foreach (var moduleKey in _settingsTypes.Keys)
179              {
180                  try
181                  {
182                      var settings = GetSettings(moduleKey);
183                      if (settings != null)
184                      {
185                          result[moduleKey] = settings;
186                      }
187                  }
188                  catch (Exception ex)
189                  {
190                      System.Diagnostics.Debug.WriteLine($"Error getting settings for {moduleKey}: {ex.Message}");
191                  }
192              }
193  
194              return result;
195          }
196  
197          /// <summary>
198          /// Gets a specific settings repository instance
199          /// </summary>
200          /// <typeparam name="T">The settings type</typeparam>
201          /// <returns>The settings repository instance</returns>
202          public ISettingsRepository<T> GetRepository<T>()
203              where T : class, ISettingsConfig, new()
204          {
205              return SettingsRepository<T>.GetInstance(_settingsUtils);
206          }
207      }
208  }