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 }