2020-09-23 13:20:32 -07:00
|
|
|
|
// 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;
|
2026-01-07 16:38:09 +08:00
|
|
|
|
using System.IO;
|
2024-11-13 12:36:45 -05:00
|
|
|
|
using System.Threading;
|
2026-01-07 16:38:09 +08:00
|
|
|
|
using ManagedCommon;
|
2020-10-22 09:45:48 -07:00
|
|
|
|
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
2020-09-23 13:20:32 -07:00
|
|
|
|
|
2020-10-22 09:45:48 -07:00
|
|
|
|
namespace Microsoft.PowerToys.Settings.UI.Library
|
2020-09-23 13:20:32 -07:00
|
|
|
|
{
|
|
|
|
|
|
// This Singleton class is a wrapper around the settings configurations that are accessed by viewmodels.
|
|
|
|
|
|
// This class can have only one instance and therefore the settings configurations are common to all.
|
2026-01-07 16:38:09 +08:00
|
|
|
|
public sealed class SettingsRepository<T> : ISettingsRepository<T>, IDisposable
|
2020-09-23 13:20:32 -07:00
|
|
|
|
where T : class, ISettingsConfig, new()
|
|
|
|
|
|
{
|
2024-11-13 12:36:45 -05:00
|
|
|
|
private static readonly Lock _SettingsRepoLock = new Lock();
|
2020-09-23 13:20:32 -07:00
|
|
|
|
|
2025-12-19 03:30:01 +01:00
|
|
|
|
private static SettingsUtils _settingsUtils;
|
2020-09-23 13:20:32 -07:00
|
|
|
|
|
|
|
|
|
|
private static SettingsRepository<T> settingsRepository;
|
|
|
|
|
|
|
|
|
|
|
|
private T settingsConfig;
|
|
|
|
|
|
|
2026-01-07 16:38:09 +08:00
|
|
|
|
private FileSystemWatcher _watcher;
|
|
|
|
|
|
|
|
|
|
|
|
public event Action<T> SettingsChanged;
|
|
|
|
|
|
|
2020-10-19 13:32:05 -07:00
|
|
|
|
// Suppressing the warning as this is a singleton class and this method is
|
|
|
|
|
|
// necessarily static
|
|
|
|
|
|
#pragma warning disable CA1000 // Do not declare static members on generic types
|
2025-12-19 03:30:01 +01:00
|
|
|
|
public static SettingsRepository<T> GetInstance(SettingsUtils settingsUtils)
|
2020-10-19 13:32:05 -07:00
|
|
|
|
#pragma warning restore CA1000 // Do not declare static members on generic types
|
2020-09-23 13:20:32 -07:00
|
|
|
|
{
|
|
|
|
|
|
// To ensure that only one instance of Settings Repository is created in a multi-threaded environment.
|
|
|
|
|
|
lock (_SettingsRepoLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (settingsRepository == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
settingsRepository = new SettingsRepository<T>();
|
|
|
|
|
|
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
|
2026-01-07 16:38:09 +08:00
|
|
|
|
settingsRepository.InitializeWatcher();
|
2020-09-23 13:20:32 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return settingsRepository;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The Singleton class must have a private constructor so that it cannot be instantiated by any other object other than itself.
|
|
|
|
|
|
private SettingsRepository()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-07 16:38:09 +08:00
|
|
|
|
private void InitializeWatcher()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var settingsItem = new T();
|
|
|
|
|
|
var filePath = _settingsUtils.GetSettingsFilePath(settingsItem.GetModuleName());
|
|
|
|
|
|
var directory = Path.GetDirectoryName(filePath);
|
|
|
|
|
|
var fileName = Path.GetFileName(filePath);
|
|
|
|
|
|
|
|
|
|
|
|
if (!Directory.Exists(directory))
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.CreateDirectory(directory);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_watcher = new FileSystemWatcher(directory, fileName);
|
|
|
|
|
|
_watcher.NotifyFilter = NotifyFilters.LastWrite;
|
|
|
|
|
|
_watcher.Changed += Watcher_Changed;
|
|
|
|
|
|
_watcher.EnableRaisingEvents = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.LogError($"Failed to initialize settings watcher for {typeof(T).Name}", ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Watcher_Changed(object sender, FileSystemEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Wait a bit for the file write to complete and retry if needed
|
|
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
Thread.Sleep(100);
|
|
|
|
|
|
if (ReloadSettings())
|
|
|
|
|
|
{
|
|
|
|
|
|
SettingsChanged?.Invoke(SettingsConfig);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-03-15 01:42:47 -07:00
|
|
|
|
public bool ReloadSettings()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
T settingsItem = new T();
|
2026-01-07 16:38:09 +08:00
|
|
|
|
settingsConfig = _settingsUtils.GetSettings<T>(settingsItem.GetModuleName());
|
2023-03-15 01:42:47 -07:00
|
|
|
|
|
|
|
|
|
|
SettingsConfig = settingsConfig;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-23 13:20:32 -07:00
|
|
|
|
// Settings configurations shared across all viewmodels
|
|
|
|
|
|
public T SettingsConfig
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (settingsConfig == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
T settingsItem = new T();
|
2021-01-14 14:14:29 +02:00
|
|
|
|
settingsConfig = _settingsUtils.GetSettingsOrDefault<T>(settingsItem.GetModuleName());
|
2020-09-23 13:20:32 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return settingsConfig;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (value != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
settingsConfig = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-07 16:38:09 +08:00
|
|
|
|
|
|
|
|
|
|
public void StopWatching()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_watcher != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_watcher.EnableRaisingEvents = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void StartWatching()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_watcher != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_watcher.EnableRaisingEvents = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
_watcher?.Dispose();
|
|
|
|
|
|
}
|
2020-09-23 13:20:32 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|