/ src / settings-ui / Settings.UI / ViewModels / GeneralViewModel.cs
GeneralViewModel.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.Collections.ObjectModel;
   8  using System.Diagnostics;
   9  using System.Globalization;
  10  using System.IO;
  11  using System.IO.Abstractions;
  12  using System.Linq;
  13  using System.Reflection;
  14  using System.Runtime.CompilerServices;
  15  using System.Text.Json;
  16  using System.Text.Json.Serialization.Metadata;
  17  using System.Threading.Tasks;
  18  
  19  using global::PowerToys.GPOWrapper;
  20  using ManagedCommon;
  21  using Microsoft.PowerToys.Settings.UI.Helpers;
  22  using Microsoft.PowerToys.Settings.UI.Library;
  23  using Microsoft.PowerToys.Settings.UI.Library.Helpers;
  24  using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
  25  using Microsoft.PowerToys.Settings.UI.Library.Utilities;
  26  using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
  27  using Microsoft.PowerToys.Settings.UI.SerializationContext;
  28  using Microsoft.PowerToys.Telemetry;
  29  using Microsoft.Win32;
  30  using Windows.System.Profile;
  31  
  32  namespace Microsoft.PowerToys.Settings.UI.ViewModels
  33  {
  34      public partial class GeneralViewModel : PageViewModelBase
  35      {
  36          public enum InstallScope
  37          {
  38              PerMachine = 0,
  39              PerUser,
  40          }
  41  
  42          protected override string ModuleName => "GeneralSettings";
  43  
  44          public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()
  45          {
  46              return new Dictionary<string, HotkeySettings[]>
  47              {
  48                  { ModuleName, new HotkeySettings[] { QuickAccessShortcut } },
  49              };
  50          }
  51  
  52          private GeneralSettings GeneralSettingsConfig { get; set; }
  53  
  54          private UpdatingSettings UpdatingSettingsConfig { get; set; }
  55  
  56          public ButtonClickCommand CheckForUpdatesEventHandler { get; set; }
  57  
  58          public Windows.ApplicationModel.Resources.ResourceLoader ResourceLoader { get; set; }
  59  
  60          private Action HideBackupAndRestoreMessageAreaAction { get; set; }
  61  
  62          private Action<int> DoBackupAndRestoreDryRun { get; set; }
  63  
  64          public ButtonClickCommand BackupConfigsEventHandler { get; set; }
  65  
  66          public ButtonClickCommand RestoreConfigsEventHandler { get; set; }
  67  
  68          public ButtonClickCommand RefreshBackupStatusEventHandler { get; set; }
  69  
  70          public ButtonClickCommand SelectSettingBackupDirEventHandler { get; set; }
  71  
  72          public ButtonClickCommand RestartElevatedButtonEventHandler { get; set; }
  73  
  74          public ButtonClickCommand UpdateNowButtonEventHandler { get; set; }
  75  
  76          public Func<string, int> SendConfigMSG { get; }
  77  
  78          public Func<string, int> SendRestartAsAdminConfigMSG { get; }
  79  
  80          public Func<string, int> SendCheckForUpdatesConfigMSG { get; }
  81  
  82          public string RunningAsUserDefaultText { get; set; }
  83  
  84          public string RunningAsAdminDefaultText { get; set; }
  85  
  86          private string _settingsConfigFileFolder = string.Empty;
  87  
  88          private ISettingsRepository<GeneralSettings> _settingsRepository;
  89          private Microsoft.UI.Dispatching.DispatcherQueue _dispatcherQueue;
  90  
  91          private IFileSystemWatcher _fileWatcher;
  92  
  93          private Func<Task<string>> PickSingleFolderDialog { get; }
  94  
  95          private SettingsBackupAndRestoreUtils settingsBackupAndRestoreUtils = SettingsBackupAndRestoreUtils.Instance;
  96  
  97          private const string InstallScopeRegKey = @"Software\Classes\powertoys\";
  98  
  99          public GeneralViewModel(ISettingsRepository<GeneralSettings> settingsRepository, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func<string, int> ipcMSGCallBackFunc, Func<string, int> ipcMSGRestartAsAdminMSGCallBackFunc, Func<string, int> ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "", Action dispatcherAction = null, Action hideBackupAndRestoreMessageAreaAction = null, Action<int> doBackupAndRestoreDryRun = null, Func<Task<string>> pickSingleFolderDialog = null, Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = null)
 100          {
 101              CheckForUpdatesEventHandler = new ButtonClickCommand(CheckForUpdatesClick);
 102              RestartElevatedButtonEventHandler = new ButtonClickCommand(RestartElevated);
 103              UpdateNowButtonEventHandler = new ButtonClickCommand(UpdateNowClick);
 104              BackupConfigsEventHandler = new ButtonClickCommand(BackupConfigsClick);
 105              SelectSettingBackupDirEventHandler = new ButtonClickCommand(SelectSettingBackupDir);
 106              RestoreConfigsEventHandler = new ButtonClickCommand(RestoreConfigsClick);
 107              RefreshBackupStatusEventHandler = new ButtonClickCommand(RefreshBackupStatusEventHandlerClick);
 108              HideBackupAndRestoreMessageAreaAction = hideBackupAndRestoreMessageAreaAction;
 109              DoBackupAndRestoreDryRun = doBackupAndRestoreDryRun;
 110              PickSingleFolderDialog = pickSingleFolderDialog;
 111              ResourceLoader = resourceLoader;
 112  
 113              // To obtain the general settings configuration of PowerToys if it exists, else to create a new file and return the default configurations.
 114              ArgumentNullException.ThrowIfNull(settingsRepository);
 115  
 116              _settingsRepository = settingsRepository;
 117              _settingsRepository.SettingsChanged += OnSettingsChanged;
 118              _dispatcherQueue = GetDispatcherQueue();
 119  
 120              GeneralSettingsConfig = settingsRepository.SettingsConfig;
 121              UpdatingSettingsConfig = UpdatingSettings.LoadSettings();
 122              if (UpdatingSettingsConfig == null)
 123              {
 124                  UpdatingSettingsConfig = new UpdatingSettings();
 125              }
 126  
 127              // set the callback functions value to handle outgoing IPC message.
 128              SendConfigMSG = ipcMSGCallBackFunc;
 129              SendCheckForUpdatesConfigMSG = ipcMSGCheckForUpdatesCallBackFunc;
 130              SendRestartAsAdminConfigMSG = ipcMSGRestartAsAdminMSGCallBackFunc;
 131  
 132              // Update Settings file folder:
 133              _settingsConfigFileFolder = configFileSubfolder;
 134  
 135              // Using Invariant here as these are internal strings and the analyzer
 136              // expects strings to be normalized to uppercase. While the theme names
 137              // are represented in lowercase everywhere else, we'll use uppercase
 138              // normalization for switch statements
 139              switch (GeneralSettingsConfig.Theme.ToUpperInvariant())
 140              {
 141                  case "DARK":
 142                      _themeIndex = 0;
 143                      break;
 144                  case "LIGHT":
 145                      _themeIndex = 1;
 146                      break;
 147                  case "SYSTEM":
 148                      _themeIndex = 2;
 149                      break;
 150              }
 151  
 152              _isDevBuild = Helper.GetProductVersion() == "v0.0.1";
 153  
 154              _runAtStartupGpoRuleConfiguration = GPOWrapper.GetConfiguredRunAtStartupValue();
 155              if (_runAtStartupGpoRuleConfiguration == GpoRuleConfigured.Disabled || _runAtStartupGpoRuleConfiguration == GpoRuleConfigured.Enabled)
 156              {
 157                  // Get the enabled state from GPO.
 158                  _runAtStartupIsGPOConfigured = true;
 159                  _startup = _runAtStartupGpoRuleConfiguration == GpoRuleConfigured.Enabled;
 160              }
 161              else
 162              {
 163                  _startup = GeneralSettingsConfig.Startup;
 164              }
 165  
 166              _showSysTrayIcon = GeneralSettingsConfig.ShowSysTrayIcon;
 167              _showThemeAdaptiveSysTrayIcon = GeneralSettingsConfig.ShowThemeAdaptiveTrayIcon;
 168              _showNewUpdatesToastNotification = GeneralSettingsConfig.ShowNewUpdatesToastNotification;
 169              _autoDownloadUpdates = GeneralSettingsConfig.AutoDownloadUpdates;
 170              _showWhatsNewAfterUpdates = GeneralSettingsConfig.ShowWhatsNewAfterUpdates;
 171              _enableExperimentation = GeneralSettingsConfig.EnableExperimentation;
 172  
 173              _isElevated = isElevated;
 174              _runElevated = GeneralSettingsConfig.RunElevated;
 175              _enableWarningsElevatedApps = GeneralSettingsConfig.EnableWarningsElevatedApps;
 176              _enableQuickAccess = GeneralSettingsConfig.EnableQuickAccess;
 177              _quickAccessShortcut = GeneralSettingsConfig.QuickAccessShortcut;
 178              if (_quickAccessShortcut != null)
 179              {
 180                  _quickAccessShortcut.PropertyChanged += QuickAccessShortcut_PropertyChanged;
 181              }
 182  
 183              RunningAsUserDefaultText = runAsUserText;
 184              RunningAsAdminDefaultText = runAsAdminText;
 185  
 186              _isAdmin = isAdmin;
 187  
 188              _updatingState = UpdatingSettingsConfig.State;
 189              _newAvailableVersion = UpdatingSettingsConfig.NewVersion;
 190              _newAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink;
 191              _updateCheckedDate = UpdatingSettingsConfig.LastCheckedDateLocalized;
 192  
 193              _newUpdatesToastIsGpoDisabled = GPOWrapper.GetDisableNewUpdateToastValue() == GpoRuleConfigured.Enabled;
 194              _autoDownloadUpdatesIsGpoDisabled = GPOWrapper.GetDisableAutomaticUpdateDownloadValue() == GpoRuleConfigured.Enabled;
 195              _experimentationIsGpoDisallowed = GPOWrapper.GetAllowExperimentationValue() == GpoRuleConfigured.Disabled;
 196              _showWhatsNewAfterUpdatesIsGpoDisabled = GPOWrapper.GetDisableShowWhatsNewAfterUpdatesValue() == GpoRuleConfigured.Enabled;
 197              _enableDataDiagnosticsIsGpoDisallowed = GPOWrapper.GetAllowDataDiagnosticsValue() == GpoRuleConfigured.Disabled;
 198  
 199              if (_enableDataDiagnosticsIsGpoDisallowed)
 200              {
 201                  _enableDataDiagnostics = false;
 202              }
 203              else
 204              {
 205                  _enableDataDiagnostics = DataDiagnosticsSettings.GetEnabledValue();
 206              }
 207  
 208              _enableViewDataDiagnostics = DataDiagnosticsSettings.GetViewEnabledValue();
 209              _enableViewDataDiagnosticsOnLoad = _enableViewDataDiagnostics;
 210  
 211              if (dispatcherAction != null)
 212              {
 213                  _fileWatcher = Helper.GetFileWatcher(string.Empty, UpdatingSettings.SettingsFile, dispatcherAction);
 214              }
 215  
 216              // Diagnostic data retention policy
 217              string etwDirPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\etw");
 218              DeleteDiagnosticDataOlderThan28Days(etwDirPath);
 219  
 220              string localLowEtwDirPath = Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), "AppData", "LocalLow", "Microsoft", "PowerToys", "etw");
 221              DeleteDiagnosticDataOlderThan28Days(localLowEtwDirPath);
 222  
 223              InitializeLanguages();
 224          }
 225  
 226          // Supported languages. Taken from Resources.wxs + default + en-US
 227          private Dictionary<string, string> langTagsAndIds = new Dictionary<string, string>
 228          {
 229              { string.Empty, "Default_language" },
 230              { "ar-SA", "Arabic_Saudi_Arabia_Language" },
 231              { "cs-CZ", "Czech_Language" },
 232              { "de-DE", "German_Language" },
 233              { "en-US", "English_Language" },
 234              { "es-ES", "Spanish_Language" },
 235              { "fa-IR", "Persian_Farsi_Language" },
 236              { "fr-FR", "French_Language" },
 237              { "he-IL", "Hebrew_Israel_Language" },
 238              { "hu-HU", "Hungarian_Language" },
 239              { "it-IT", "Italian_Language" },
 240              { "ja-JP", "Japanese_Language" },
 241              { "ko-KR", "Korean_Language" },
 242              { "nl-NL", "Dutch_Language" },
 243              { "pl-PL", "Polish_Language" },
 244              { "pt-BR", "Portuguese_Brazil_Language" },
 245              { "pt-PT", "Portuguese_Portugal_Language" },
 246              { "ru-RU", "Russian_Language" },
 247              { "sv-SE", "Swedish_Language" },
 248              { "tr-TR", "Turkish_Language" },
 249              { "uk-UA", "Ukrainian_Language" },
 250              { "zh-CN", "Chinese_Simplified_Language" },
 251              { "zh-TW", "Chinese_Traditional_Language" },
 252          };
 253  
 254          private static bool _isDevBuild;
 255          private bool _startup;
 256          private bool _showSysTrayIcon;
 257          private bool _showThemeAdaptiveSysTrayIcon;
 258          private GpoRuleConfigured _runAtStartupGpoRuleConfiguration;
 259          private bool _runAtStartupIsGPOConfigured;
 260          private bool _isElevated;
 261          private bool _runElevated;
 262          private bool _isAdmin;
 263          private bool _enableWarningsElevatedApps;
 264          private bool _enableQuickAccess;
 265          private HotkeySettings _quickAccessShortcut;
 266          private int _themeIndex;
 267  
 268          private bool _showNewUpdatesToastNotification;
 269          private bool _newUpdatesToastIsGpoDisabled;
 270          private bool _autoDownloadUpdates;
 271          private bool _autoDownloadUpdatesIsGpoDisabled;
 272          private bool _showWhatsNewAfterUpdates;
 273          private bool _showWhatsNewAfterUpdatesIsGpoDisabled;
 274          private bool _enableExperimentation;
 275          private bool _experimentationIsGpoDisallowed;
 276          private bool _enableDataDiagnostics;
 277          private bool _enableDataDiagnosticsIsGpoDisallowed;
 278          private bool _enableViewDataDiagnostics;
 279          private bool _enableViewDataDiagnosticsOnLoad;
 280          private bool _viewDiagnosticDataViewerChanged;
 281  
 282          private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate;
 283          private string _newAvailableVersion = string.Empty;
 284          private string _newAvailableVersionLink = string.Empty;
 285          private string _updateCheckedDate = string.Empty;
 286  
 287          private bool _isNewVersionDownloading;
 288          private bool _isNewVersionChecked;
 289          private bool _isNoNetwork;
 290          private bool _isBugReportRunning;
 291  
 292          private bool _settingsBackupRestoreMessageVisible;
 293          private string _settingsBackupMessage;
 294          private string _backupRestoreMessageSeverity;
 295  
 296          private int _languagesIndex;
 297          private int _initLanguagesIndex;
 298          private bool _languageChanged;
 299  
 300          private string reportBugLink;
 301  
 302          // Gets or sets a value indicating whether run powertoys on start-up.
 303          public string ReportBugLink
 304          {
 305              get => reportBugLink;
 306              set
 307              {
 308                  reportBugLink = value;
 309                  OnPropertyChanged(nameof(ReportBugLink));
 310              }
 311          }
 312  
 313          public void InitializeReportBugLink()
 314          {
 315              var version = GetPowerToysVersion();
 316  
 317              string isElevatedString = "PowerToys is running " + (IsElevated ? "as admin (elevated)" : "as user (non-elevated)");
 318  
 319              string installScope = GetCurrentInstallScope() == InstallScope.PerMachine ? "per machine (system)" : "per user";
 320  
 321              var info = $"OS Version: {GetOSVersion()} \n.NET Version: {GetDotNetVersion()}\n{isElevatedString}\nInstall scope: {installScope}\nOperating System Language: {CultureInfo.InstalledUICulture.DisplayName}\nSystem locale: {CultureInfo.InstalledUICulture.Name}";
 322  
 323              var gitHubURL = "https://github.com/microsoft/PowerToys/issues/new?template=bug_report.yml&labels=Issue-Bug%2CTriage-Needed" +
 324                  "&version=" + version + "&additionalInfo=" + System.Web.HttpUtility.UrlEncode(info);
 325  
 326              ReportBugLink = gitHubURL;
 327          }
 328  
 329          private string GetPowerToysVersion()
 330          {
 331              return Helper.GetProductVersion().TrimStart('v');
 332          }
 333  
 334          private string GetOSVersion()
 335          {
 336              return Environment.OSVersion.VersionString;
 337          }
 338  
 339          public static string GetDotNetVersion()
 340          {
 341              return $".NET {Environment.Version}";
 342          }
 343  
 344          public static InstallScope GetCurrentInstallScope()
 345          {
 346              // Check HKLM first
 347              if (Registry.LocalMachine.OpenSubKey(InstallScopeRegKey) != null)
 348              {
 349                  return InstallScope.PerMachine;
 350              }
 351  
 352              // If not found, check HKCU
 353              var userKey = Registry.CurrentUser.OpenSubKey(InstallScopeRegKey);
 354              if (userKey != null)
 355              {
 356                  var installScope = userKey.GetValue("InstallScope") as string;
 357                  userKey.Close();
 358                  if (!string.IsNullOrEmpty(installScope) && installScope.Contains("perUser"))
 359                  {
 360                      return InstallScope.PerUser;
 361                  }
 362              }
 363  
 364              return InstallScope.PerMachine; // Default if no specific registry key found
 365          }
 366  
 367          // Gets or sets a value indicating whether run powertoys on start-up.
 368          public bool Startup
 369          {
 370              get
 371              {
 372                  return _startup;
 373              }
 374  
 375              set
 376              {
 377                  if (_runAtStartupIsGPOConfigured)
 378                  {
 379                      // If it's GPO configured, shouldn't be able to change this state.
 380                      return;
 381                  }
 382  
 383                  if (_startup != value)
 384                  {
 385                      _startup = value;
 386                      GeneralSettingsConfig.Startup = value;
 387                      NotifyPropertyChanged();
 388                  }
 389              }
 390          }
 391  
 392          // Gets or sets a value indicating whether the PowerToys icon should be shown in the system tray.
 393          public bool ShowSysTrayIcon
 394          {
 395              get
 396              {
 397                  return _showSysTrayIcon;
 398              }
 399  
 400              set
 401              {
 402                  if (_showSysTrayIcon != value)
 403                  {
 404                      _showSysTrayIcon = value;
 405                      GeneralSettingsConfig.ShowSysTrayIcon = value;
 406                      NotifyPropertyChanged();
 407                  }
 408              }
 409          }
 410  
 411          public bool ShowThemeAdaptiveTrayIcon
 412          {
 413              get
 414              {
 415                  return _showThemeAdaptiveSysTrayIcon;
 416              }
 417  
 418              set
 419              {
 420                  if (_showThemeAdaptiveSysTrayIcon != value)
 421                  {
 422                      _showThemeAdaptiveSysTrayIcon = value;
 423                      GeneralSettingsConfig.ShowThemeAdaptiveTrayIcon = value;
 424                      NotifyPropertyChanged();
 425                  }
 426              }
 427          }
 428  
 429          public string RunningAsText
 430          {
 431              get
 432              {
 433                  if (!IsElevated)
 434                  {
 435                      return RunningAsUserDefaultText;
 436                  }
 437                  else
 438                  {
 439                      return RunningAsAdminDefaultText;
 440                  }
 441              }
 442  
 443              set
 444              {
 445                  OnPropertyChanged("RunningAsAdminText");
 446              }
 447          }
 448  
 449          // Gets or sets a value indicating whether the powertoy elevated.
 450          public bool IsElevated
 451          {
 452              get
 453              {
 454                  return _isElevated;
 455              }
 456  
 457              set
 458              {
 459                  if (_isElevated != value)
 460                  {
 461                      _isElevated = value;
 462                      OnPropertyChanged(nameof(IsElevated));
 463                      OnPropertyChanged(nameof(IsAdminButtonEnabled));
 464                      OnPropertyChanged("RunningAsAdminText");
 465                  }
 466              }
 467          }
 468  
 469          public bool IsAdminButtonEnabled
 470          {
 471              get
 472              {
 473                  return !IsElevated;
 474              }
 475  
 476              set
 477              {
 478                  OnPropertyChanged(nameof(IsAdminButtonEnabled));
 479              }
 480          }
 481  
 482          // Gets or sets a value indicating whether powertoys should run elevated.
 483          public bool RunElevated
 484          {
 485              get
 486              {
 487                  return _runElevated;
 488              }
 489  
 490              set
 491              {
 492                  if (_runElevated != value)
 493                  {
 494                      _runElevated = value;
 495                      GeneralSettingsConfig.RunElevated = value;
 496                      NotifyPropertyChanged();
 497                  }
 498              }
 499          }
 500  
 501          // Gets a value indicating whether the user is part of administrators group.
 502          public bool IsAdmin
 503          {
 504              get
 505              {
 506                  return _isAdmin;
 507              }
 508          }
 509  
 510          public bool EnableWarningsElevatedApps
 511          {
 512              get
 513              {
 514                  return _enableWarningsElevatedApps;
 515              }
 516  
 517              set
 518              {
 519                  if (_enableWarningsElevatedApps != value)
 520                  {
 521                      _enableWarningsElevatedApps = value;
 522                      GeneralSettingsConfig.EnableWarningsElevatedApps = value;
 523                      NotifyPropertyChanged();
 524                  }
 525              }
 526          }
 527  
 528          public bool EnableQuickAccess
 529          {
 530              get
 531              {
 532                  return _enableQuickAccess;
 533              }
 534  
 535              set
 536              {
 537                  if (_enableQuickAccess != value)
 538                  {
 539                      _enableQuickAccess = value;
 540                      GeneralSettingsConfig.EnableQuickAccess = value;
 541                      NotifyPropertyChanged();
 542                  }
 543              }
 544          }
 545  
 546          public HotkeySettings QuickAccessShortcut
 547          {
 548              get
 549              {
 550                  return _quickAccessShortcut;
 551              }
 552  
 553              set
 554              {
 555                  if (_quickAccessShortcut != value)
 556                  {
 557                      if (_quickAccessShortcut != null)
 558                      {
 559                          _quickAccessShortcut.PropertyChanged -= QuickAccessShortcut_PropertyChanged;
 560                      }
 561  
 562                      _quickAccessShortcut = value;
 563                      if (_quickAccessShortcut != null)
 564                      {
 565                          _quickAccessShortcut.PropertyChanged += QuickAccessShortcut_PropertyChanged;
 566                      }
 567  
 568                      GeneralSettingsConfig.QuickAccessShortcut = value;
 569                      NotifyPropertyChanged();
 570                  }
 571              }
 572          }
 573  
 574          private void QuickAccessShortcut_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
 575          {
 576              NotifyPropertyChanged(nameof(QuickAccessShortcut));
 577          }
 578  
 579          public bool SomeUpdateSettingsAreGpoManaged
 580          {
 581              get
 582              {
 583                  return _newUpdatesToastIsGpoDisabled ||
 584                      (_isAdmin && _autoDownloadUpdatesIsGpoDisabled) ||
 585                      _showWhatsNewAfterUpdatesIsGpoDisabled;
 586              }
 587          }
 588  
 589          public bool ShowNewUpdatesToastNotification
 590          {
 591              get
 592              {
 593                  return _showNewUpdatesToastNotification && !_newUpdatesToastIsGpoDisabled;
 594              }
 595  
 596              set
 597              {
 598                  if (_showNewUpdatesToastNotification != value)
 599                  {
 600                      _showNewUpdatesToastNotification = value;
 601                      GeneralSettingsConfig.ShowNewUpdatesToastNotification = value;
 602                      NotifyPropertyChanged();
 603                  }
 604              }
 605          }
 606  
 607          public bool IsShowNewUpdatesToastNotificationCardEnabled
 608          {
 609              get => !_isDevBuild && !_newUpdatesToastIsGpoDisabled;
 610          }
 611  
 612          public bool AutoDownloadUpdates
 613          {
 614              get
 615              {
 616                  return _autoDownloadUpdates && !_autoDownloadUpdatesIsGpoDisabled;
 617              }
 618  
 619              set
 620              {
 621                  if (_autoDownloadUpdates != value)
 622                  {
 623                      _autoDownloadUpdates = value;
 624                      GeneralSettingsConfig.AutoDownloadUpdates = value;
 625                      NotifyPropertyChanged();
 626                  }
 627              }
 628          }
 629  
 630          public bool IsAutoDownloadUpdatesCardEnabled
 631          {
 632              get => !_isDevBuild && !_autoDownloadUpdatesIsGpoDisabled;
 633          }
 634  
 635          public bool ShowWhatsNewAfterUpdates
 636          {
 637              get
 638              {
 639                  return _showWhatsNewAfterUpdates && !_showWhatsNewAfterUpdatesIsGpoDisabled;
 640              }
 641  
 642              set
 643              {
 644                  if (_showWhatsNewAfterUpdates != value)
 645                  {
 646                      _showWhatsNewAfterUpdates = value;
 647                      GeneralSettingsConfig.ShowWhatsNewAfterUpdates = value;
 648                      NotifyPropertyChanged();
 649                  }
 650              }
 651          }
 652  
 653          public bool IsShowWhatsNewAfterUpdatesCardEnabled
 654          {
 655              get => !_isDevBuild && !_showWhatsNewAfterUpdatesIsGpoDisabled;
 656          }
 657  
 658          public bool EnableExperimentation
 659          {
 660              get
 661              {
 662                  return _enableExperimentation && !_experimentationIsGpoDisallowed;
 663              }
 664  
 665              set
 666              {
 667                  if (_enableExperimentation != value)
 668                  {
 669                      _enableExperimentation = value;
 670                      GeneralSettingsConfig.EnableExperimentation = value;
 671                      NotifyPropertyChanged();
 672                  }
 673              }
 674          }
 675  
 676          public bool EnableDataDiagnostics
 677          {
 678              get
 679              {
 680                  return _enableDataDiagnostics;
 681              }
 682  
 683              set
 684              {
 685                  if (_enableDataDiagnostics != value)
 686                  {
 687                      _enableDataDiagnostics = value;
 688  
 689                      if (_enableDataDiagnostics == false)
 690                      {
 691                          EnableViewDataDiagnostics = false;
 692                      }
 693  
 694                      DataDiagnosticsSettings.SetEnabledValue(_enableDataDiagnostics);
 695                      NotifyPropertyChanged();
 696                  }
 697              }
 698          }
 699  
 700          public bool ViewDiagnosticDataViewerChanged
 701          {
 702              get => _viewDiagnosticDataViewerChanged;
 703          }
 704  
 705          public bool EnableViewDataDiagnostics
 706          {
 707              get
 708              {
 709                  return _enableViewDataDiagnostics;
 710              }
 711  
 712              set
 713              {
 714                  if (_enableViewDataDiagnostics != value)
 715                  {
 716                      _enableViewDataDiagnostics = value;
 717  
 718                      if (_enableViewDataDiagnostics != _enableViewDataDiagnosticsOnLoad)
 719                      {
 720                          _viewDiagnosticDataViewerChanged = true;
 721                      }
 722                      else
 723                      {
 724                          _viewDiagnosticDataViewerChanged = false;
 725                      }
 726  
 727                      DataDiagnosticsSettings.SetViewEnabledValue(_enableViewDataDiagnostics);
 728                      OnPropertyChanged(nameof(EnableViewDataDiagnostics));
 729                      OnPropertyChanged(nameof(ViewDiagnosticDataViewerChanged));
 730                  }
 731              }
 732          }
 733  
 734          public bool IsExperimentationGpoDisallowed
 735          {
 736              get => _experimentationIsGpoDisallowed;
 737          }
 738  
 739          public bool IsDataDiagnosticsGPOManaged
 740          {
 741              get => _enableDataDiagnosticsIsGpoDisallowed;
 742          }
 743  
 744          public bool IsRunAtStartupGPOManaged
 745          {
 746              get => _runAtStartupIsGPOConfigured;
 747          }
 748  
 749          public string SettingsBackupAndRestoreDir
 750          {
 751              get
 752              {
 753                  return settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir();
 754              }
 755  
 756              set
 757              {
 758                  if (settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir() != value)
 759                  {
 760                      SettingsBackupAndRestoreUtils.SetRegSettingsBackupAndRestoreItem("SettingsBackupAndRestoreDir", value);
 761                      NotifyPropertyChanged();
 762                  }
 763              }
 764          }
 765  
 766          public int ThemeIndex
 767          {
 768              get
 769              {
 770                  return _themeIndex;
 771              }
 772  
 773              set
 774              {
 775                  if (_themeIndex != value)
 776                  {
 777                      switch (value)
 778                      {
 779                          case 0: GeneralSettingsConfig.Theme = "dark"; break;
 780                          case 1: GeneralSettingsConfig.Theme = "light"; break;
 781                          case 2: GeneralSettingsConfig.Theme = "system"; break;
 782                      }
 783  
 784                      _themeIndex = value;
 785  
 786                      App.ThemeService.ApplyTheme();
 787  
 788                      NotifyPropertyChanged();
 789                  }
 790              }
 791          }
 792  
 793          public string PowerToysVersion
 794          {
 795              get
 796              {
 797                  return Helper.GetProductVersion();
 798              }
 799          }
 800  
 801          public string UpdateCheckedDate
 802          {
 803              get
 804              {
 805                  RequestUpdateCheckedDate();
 806                  return _updateCheckedDate;
 807              }
 808  
 809              set
 810              {
 811                  if (_updateCheckedDate != value)
 812                  {
 813                      _updateCheckedDate = value;
 814                      NotifyPropertyChanged();
 815                  }
 816              }
 817          }
 818  
 819          public string LastSettingsBackupDate
 820          {
 821              get
 822              {
 823                  try
 824                  {
 825                      var manifest = settingsBackupAndRestoreUtils.GetLatestSettingsBackupManifest();
 826                      if (manifest != null)
 827                      {
 828                          if (manifest["CreateDateTime"] != null)
 829                          {
 830                              if (DateTime.TryParse(manifest["CreateDateTime"].ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var theDateTime))
 831                              {
 832                                  return theDateTime.ToString("G", CultureInfo.CurrentCulture);
 833                              }
 834                              else
 835                              {
 836                                  Logger.LogError("Failed to parse time from backup");
 837                                  return GetResourceString("General_SettingsBackupAndRestore_FailedToParseTime");
 838                              }
 839                          }
 840                          else
 841                          {
 842                              return GetResourceString("General_SettingsBackupAndRestore_UnknownBackupTime");
 843                          }
 844                      }
 845                      else
 846                      {
 847                          return GetResourceString("General_SettingsBackupAndRestore_NoBackupFound");
 848                      }
 849                  }
 850                  catch (Exception e)
 851                  {
 852                      Logger.LogError("Error getting LastSettingsBackupDate", e);
 853                      return GetResourceString("General_SettingsBackupAndRestore_UnknownBackupTime");
 854                  }
 855              }
 856          }
 857  
 858          public string CurrentSettingMatchText
 859          {
 860              get
 861              {
 862                  try
 863                  {
 864                      var results = settingsBackupAndRestoreUtils.GetLastBackupSettingsResults();
 865  
 866                      var resultText = string.Empty;
 867  
 868                      if (!results.LastRan.HasValue)
 869                      {
 870                          // not ran since started.
 871                          return GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsNoChecked"); // "Current Settings Unknown";
 872                      }
 873                      else
 874                      {
 875                          if (results.Success)
 876                          {
 877                              if (results.LastBackupExists)
 878                              {
 879                                  // if true, it means a backup would have been made
 880                                  resultText = GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsDiffer"); // "Current Settings Differ";
 881                              }
 882                              else
 883                              {
 884                                  // would have done the backup, but there also was not an existing one there.
 885                                  resultText = GetResourceString("General_SettingsBackupAndRestore_NoBackupFound");
 886                              }
 887                          }
 888                          else
 889                          {
 890                              if (results.HadError)
 891                              {
 892                                  // if false and error we don't really know
 893                                  resultText = GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsUnknown"); // "Current Settings Unknown";
 894                              }
 895                              else
 896                              {
 897                                  // if false, it means a backup would not have been needed/made
 898                                  resultText = GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsMatch"); // "Current Settings Match";
 899                              }
 900                          }
 901  
 902                          return $"{resultText} {GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsStatusAt")} {results.LastRan.Value.ToLocalTime().ToString("G", CultureInfo.CurrentCulture)}";
 903                      }
 904                  }
 905                  catch (Exception e)
 906                  {
 907                      Logger.LogError("Error getting CurrentSettingMatchText", e);
 908                      return string.Empty;
 909                  }
 910              }
 911          }
 912  
 913          public string LastSettingsBackupSource
 914          {
 915              get
 916              {
 917                  try
 918                  {
 919                      var manifest = settingsBackupAndRestoreUtils.GetLatestSettingsBackupManifest();
 920                      if (manifest != null)
 921                      {
 922                          if (manifest["BackupSource"] != null)
 923                          {
 924                              if (manifest["BackupSource"].ToString().Equals(Environment.MachineName, StringComparison.OrdinalIgnoreCase))
 925                              {
 926                                  return GetResourceString("General_SettingsBackupAndRestore_ThisMachine");
 927                              }
 928                              else
 929                              {
 930                                  return manifest["BackupSource"].ToString();
 931                              }
 932                          }
 933                          else
 934                          {
 935                              return GetResourceString("General_SettingsBackupAndRestore_UnknownBackupSource");
 936                          }
 937                      }
 938                      else
 939                      {
 940                          return GetResourceString("General_SettingsBackupAndRestore_NoBackupFound");
 941                      }
 942                  }
 943                  catch (Exception e)
 944                  {
 945                      Logger.LogError("Error getting LastSettingsBackupSource", e);
 946                      return GetResourceString("General_SettingsBackupAndRestore_UnknownBackupSource");
 947                  }
 948              }
 949          }
 950  
 951          public string LastSettingsBackupFileName
 952          {
 953              get
 954              {
 955                  try
 956                  {
 957                      var fileName = settingsBackupAndRestoreUtils.GetLatestBackupFileName();
 958                      return !string.IsNullOrEmpty(fileName) ? fileName : GetResourceString("General_SettingsBackupAndRestore_NoBackupFound");
 959                  }
 960                  catch (Exception e)
 961                  {
 962                      Logger.LogError("Error getting LastSettingsBackupFileName", e);
 963                      return string.Empty;
 964                  }
 965              }
 966          }
 967  
 968          public UpdatingSettings.UpdatingState PowerToysUpdatingState
 969          {
 970              get
 971              {
 972                  return _updatingState;
 973              }
 974  
 975              private set
 976              {
 977                  if (value != _updatingState)
 978                  {
 979                      _updatingState = value;
 980                      NotifyPropertyChanged();
 981                  }
 982              }
 983          }
 984  
 985          public string PowerToysNewAvailableVersion
 986          {
 987              get
 988              {
 989                  return _newAvailableVersion;
 990              }
 991  
 992              private set
 993              {
 994                  if (value != _newAvailableVersion)
 995                  {
 996                      _newAvailableVersion = value;
 997                      NotifyPropertyChanged();
 998                  }
 999              }
1000          }
1001  
1002          public string PowerToysNewAvailableVersionLink
1003          {
1004              get
1005              {
1006                  return _newAvailableVersionLink;
1007              }
1008  
1009              private set
1010              {
1011                  if (value != _newAvailableVersionLink)
1012                  {
1013                      _newAvailableVersionLink = value;
1014                      NotifyPropertyChanged();
1015                  }
1016              }
1017          }
1018  
1019          public bool IsNewVersionDownloading
1020          {
1021              get
1022              {
1023                  return _isNewVersionDownloading;
1024              }
1025  
1026              set
1027              {
1028                  if (value != _isNewVersionDownloading)
1029                  {
1030                      _isNewVersionDownloading = value;
1031                      NotifyPropertyChanged();
1032                  }
1033              }
1034          }
1035  
1036          public bool IsNewVersionCheckedAndUpToDate
1037          {
1038              get
1039              {
1040                  return _isNewVersionChecked;
1041              }
1042          }
1043  
1044          public bool IsNoNetwork
1045          {
1046              get
1047              {
1048                  return _isNoNetwork;
1049              }
1050          }
1051  
1052          public bool IsBugReportRunning
1053          {
1054              get
1055              {
1056                  return _isBugReportRunning;
1057              }
1058  
1059              set
1060              {
1061                  if (value != _isBugReportRunning)
1062                  {
1063                      _isBugReportRunning = value;
1064                      NotifyPropertyChanged();
1065                  }
1066              }
1067          }
1068  
1069          public bool SettingsBackupRestoreMessageVisible
1070          {
1071              get
1072              {
1073                  return _settingsBackupRestoreMessageVisible;
1074              }
1075          }
1076  
1077          public string BackupRestoreMessageSeverity
1078          {
1079              get
1080              {
1081                  return _backupRestoreMessageSeverity;
1082              }
1083          }
1084  
1085          public string SettingsBackupMessage
1086          {
1087              get
1088              {
1089                  return _settingsBackupMessage;
1090              }
1091          }
1092  
1093          public bool IsDownloadAllowed
1094          {
1095              get
1096              {
1097                  return !_isDevBuild && !IsNewVersionDownloading;
1098              }
1099          }
1100  
1101          public bool IsUpdatePanelVisible
1102          {
1103              get
1104              {
1105                  return PowerToysUpdatingState == UpdatingSettings.UpdatingState.UpToDate || PowerToysUpdatingState == UpdatingSettings.UpdatingState.NetworkError;
1106              }
1107          }
1108  
1109          public ObservableCollection<LanguageModel> Languages { get; } = new ObservableCollection<LanguageModel>();
1110  
1111          public int LanguagesIndex
1112          {
1113              get
1114              {
1115                  return _languagesIndex;
1116              }
1117  
1118              set
1119              {
1120                  if (_languagesIndex != value)
1121                  {
1122                      _languagesIndex = value;
1123                      OnPropertyChanged(nameof(LanguagesIndex));
1124                      NotifyLanguageChanged();
1125                      if (_initLanguagesIndex != value)
1126                      {
1127                          LanguageChanged = true;
1128                      }
1129                      else
1130                      {
1131                          LanguageChanged = false;
1132                      }
1133                  }
1134              }
1135          }
1136  
1137          public bool LanguageChanged
1138          {
1139              get
1140              {
1141                  return _languageChanged;
1142              }
1143  
1144              set
1145              {
1146                  if (_languageChanged != value)
1147                  {
1148                      _languageChanged = value;
1149                      OnPropertyChanged(nameof(LanguageChanged));
1150                  }
1151              }
1152          }
1153  
1154          public void NotifyPropertyChanged([CallerMemberName] string propertyName = null, bool reDoBackupDryRun = true)
1155          {
1156              // Notify UI of property change
1157              OnPropertyChanged(propertyName);
1158  
1159              OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
1160  
1161              SendConfigMSG(outsettings.ToString());
1162  
1163              if (reDoBackupDryRun && DoBackupAndRestoreDryRun != null)
1164              {
1165                  DoBackupAndRestoreDryRun(500);
1166              }
1167          }
1168  
1169          /// <summary>
1170          /// Method <c>SelectSettingBackupDir</c> opens folder browser to select a backup and restore location.
1171          /// </summary>
1172          private async void SelectSettingBackupDir()
1173          {
1174              var currentDir = settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir();
1175  
1176              var newPath = await PickSingleFolderDialog();
1177  
1178              if (!string.IsNullOrEmpty(newPath))
1179              {
1180                  SettingsBackupAndRestoreDir = newPath;
1181                  NotifyAllBackupAndRestoreProperties();
1182              }
1183          }
1184  
1185          private void RefreshBackupStatusEventHandlerClick()
1186          {
1187              DoBackupAndRestoreDryRun(0);
1188          }
1189  
1190          /// <summary>
1191          /// Method <c>RestoreConfigsClick</c> starts the restore.
1192          /// </summary>
1193          private void RestoreConfigsClick()
1194          {
1195              string settingsBackupAndRestoreDir = settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir();
1196  
1197              if (string.IsNullOrEmpty(settingsBackupAndRestoreDir))
1198              {
1199                  SelectSettingBackupDir();
1200              }
1201  
1202              var results = SettingsUtils.RestoreSettings();
1203              _backupRestoreMessageSeverity = results.Severity;
1204  
1205              if (!results.Success)
1206              {
1207                  _settingsBackupRestoreMessageVisible = true;
1208  
1209                  _settingsBackupMessage = GetResourceString(results.Message);
1210  
1211                  NotifyAllBackupAndRestoreProperties();
1212  
1213                  HideBackupAndRestoreMessageAreaAction();
1214              }
1215              else
1216              {
1217                  // make sure not to do NotifyPropertyChanged here, else it will persist the configs from memory and
1218                  // undo the settings restore.
1219                  SettingsBackupAndRestoreUtils.SetRegSettingsBackupAndRestoreItem("LastSettingsRestoreDate", DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture));
1220  
1221                  Restart();
1222              }
1223          }
1224  
1225          /// <summary>
1226          /// Method <c>BackupConfigsClick</c> starts the backup.
1227          /// </summary>
1228          private void BackupConfigsClick()
1229          {
1230              string settingsBackupAndRestoreDir = settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir();
1231  
1232              if (string.IsNullOrEmpty(settingsBackupAndRestoreDir))
1233              {
1234                  SelectSettingBackupDir();
1235              }
1236  
1237              var results = SettingsUtils.BackupSettings();
1238  
1239              _settingsBackupRestoreMessageVisible = true;
1240              _backupRestoreMessageSeverity = results.Severity;
1241              _settingsBackupMessage = GetResourceString(results.Message) + results.OptionalMessage;
1242  
1243              // now we do a dry run to get the results for "setting match"
1244              var settingsUtils = SettingsUtils.Default;
1245              var appBasePath = Path.GetDirectoryName(settingsUtils.GetSettingsFilePath());
1246              settingsBackupAndRestoreUtils.BackupSettings(appBasePath, settingsBackupAndRestoreDir, true);
1247  
1248              NotifyAllBackupAndRestoreProperties();
1249  
1250              HideBackupAndRestoreMessageAreaAction();
1251          }
1252  
1253          public void NotifyAllBackupAndRestoreProperties()
1254          {
1255              NotifyPropertyChanged(nameof(LastSettingsBackupDate), false);
1256              NotifyPropertyChanged(nameof(LastSettingsBackupSource), false);
1257              NotifyPropertyChanged(nameof(LastSettingsBackupFileName), false);
1258              NotifyPropertyChanged(nameof(CurrentSettingMatchText), false);
1259              NotifyPropertyChanged(nameof(SettingsBackupMessage), false);
1260              NotifyPropertyChanged(nameof(BackupRestoreMessageSeverity), false);
1261              NotifyPropertyChanged(nameof(SettingsBackupRestoreMessageVisible), false);
1262          }
1263  
1264          // callback function to launch the URL to check for updates.
1265          private void CheckForUpdatesClick()
1266          {
1267              GeneralSettingsConfig.CustomActionName = "check_for_updates";
1268  
1269              OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
1270              GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
1271  
1272              SendCheckForUpdatesConfigMSG(customaction.ToString());
1273          }
1274  
1275          private void UpdateNowClick()
1276          {
1277              IsNewVersionDownloading = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
1278              NotifyPropertyChanged(nameof(IsDownloadAllowed));
1279  
1280              Process.Start(new ProcessStartInfo(Helper.GetPowerToysInstallationFolder() + "\\PowerToys.exe") { Arguments = "powertoys://update_now/" });
1281          }
1282  
1283          /// <summary>
1284          /// Class <c>GetResourceString</c> gets a localized text.
1285          /// </summary>
1286          /// <remarks>
1287          /// To do: see if there is a betting way to do this, there should be. It does allow us to return missing localization in a way that makes it obvious they were missed.
1288          /// </remarks>
1289          public string GetResourceString(string resource)
1290          {
1291              if (ResourceLoader != null)
1292              {
1293                  var result = ResourceLoader.GetString(resource);
1294                  if (string.IsNullOrEmpty(result))
1295                  {
1296                      return resource.ToUpperInvariant() + "!!!";
1297                  }
1298                  else
1299                  {
1300                      return result;
1301                  }
1302              }
1303              else
1304              {
1305                  return resource;
1306              }
1307          }
1308  
1309          public void RequestUpdateCheckedDate()
1310          {
1311              GeneralSettingsConfig.CustomActionName = "request_update_state_date";
1312  
1313              OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
1314              GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
1315  
1316              SendCheckForUpdatesConfigMSG(customaction.ToString());
1317          }
1318  
1319          public void RestartElevated()
1320          {
1321              GeneralSettingsConfig.CustomActionName = "restart_elevation";
1322  
1323              OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
1324              GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
1325  
1326              SendRestartAsAdminConfigMSG(customaction.ToString());
1327          }
1328  
1329          /// <summary>
1330          /// Class <c>Restart</c> begin a restart and signal we want to maintain elevation
1331          /// </summary>
1332          /// <remarks>
1333          /// Other restarts either raised or lowered elevation
1334          /// </remarks>
1335          public void Restart()
1336          {
1337              GeneralSettingsConfig.CustomActionName = "restart_maintain_elevation";
1338  
1339              OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
1340              GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
1341  
1342              var dataToSend = customaction.ToString();
1343              dataToSend = JsonSerializer.Serialize(ActionMessage.Create("restart_maintain_elevation"), SourceGenerationContextContext.Default.ActionMessage);
1344              SendRestartAsAdminConfigMSG(dataToSend);
1345          }
1346  
1347          /// <summary>
1348          /// Class <c>HideBackupAndRestoreMessageArea</c> hides the backup/restore message area
1349          /// </summary>
1350          /// <remarks>
1351          /// We want to have it go away after a short period.
1352          /// </remarks>
1353          public void HideBackupAndRestoreMessageArea()
1354          {
1355              _settingsBackupRestoreMessageVisible = false;
1356              NotifyAllBackupAndRestoreProperties();
1357          }
1358  
1359          public void RefreshUpdatingState()
1360          {
1361              object oLock = new object();
1362              lock (oLock)
1363              {
1364                  var config = UpdatingSettings.LoadSettings();
1365  
1366                  // Retry loading if failed
1367                  for (int i = 0; i < 3 && config == null; i++)
1368                  {
1369                      System.Threading.Thread.Sleep(100);
1370                      config = UpdatingSettings.LoadSettings();
1371                  }
1372  
1373                  if (config == null)
1374                  {
1375                      return;
1376                  }
1377  
1378                  UpdatingSettingsConfig = config;
1379  
1380                  if (PowerToysUpdatingState != config.State)
1381                  {
1382                      IsNewVersionDownloading = false;
1383                  }
1384                  else
1385                  {
1386                      bool dateChanged = UpdateCheckedDate == UpdatingSettingsConfig.LastCheckedDateLocalized;
1387                      bool fileDownloaded = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
1388                      IsNewVersionDownloading = !(dateChanged || fileDownloaded);
1389                  }
1390  
1391                  PowerToysUpdatingState = UpdatingSettingsConfig.State;
1392                  PowerToysNewAvailableVersion = UpdatingSettingsConfig.NewVersion;
1393                  PowerToysNewAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink;
1394                  UpdateCheckedDate = UpdatingSettingsConfig.LastCheckedDateLocalized;
1395  
1396                  _isNoNetwork = PowerToysUpdatingState == UpdatingSettings.UpdatingState.NetworkError;
1397                  NotifyPropertyChanged(nameof(IsNoNetwork));
1398                  NotifyPropertyChanged(nameof(IsNewVersionDownloading));
1399                  NotifyPropertyChanged(nameof(IsUpdatePanelVisible));
1400                  _isNewVersionChecked = PowerToysUpdatingState == UpdatingSettings.UpdatingState.UpToDate && !IsNewVersionDownloading;
1401                  NotifyPropertyChanged(nameof(IsNewVersionCheckedAndUpToDate));
1402  
1403                  NotifyPropertyChanged(nameof(IsDownloadAllowed));
1404              }
1405          }
1406  
1407          private void InitializeLanguages()
1408          {
1409              var lang = LanguageModel.LoadSetting();
1410              var selectedLanguageIndex = 0;
1411  
1412              foreach (var item in langTagsAndIds)
1413              {
1414                  var language = new LanguageModel { Tag = item.Key, ResourceID = item.Value, Language = GetResourceString(item.Value) };
1415                  var index = GetLanguageIndex(language.Language, item.Key == string.Empty);
1416                  Languages.Insert(index, language);
1417  
1418                  if (item.Key.Equals(lang, StringComparison.Ordinal))
1419                  {
1420                      selectedLanguageIndex = index;
1421                  }
1422                  else if (index <= selectedLanguageIndex)
1423                  {
1424                      selectedLanguageIndex++;
1425                  }
1426              }
1427  
1428              _initLanguagesIndex = selectedLanguageIndex;
1429              LanguagesIndex = selectedLanguageIndex;
1430          }
1431  
1432          [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1309:Use ordinal string comparison", Justification = "Building a user facing list")]
1433          private int GetLanguageIndex(string language, bool isDefault)
1434          {
1435              if (Languages.Count == 0 || isDefault)
1436              {
1437                  return 0;
1438              }
1439  
1440              for (var i = 1; i < Languages.Count; i++)
1441              {
1442                  if (string.Compare(Languages[i].Language, language, StringComparison.CurrentCultureIgnoreCase) > 0)
1443                  {
1444                      return i;
1445                  }
1446              }
1447  
1448              return Languages.Count;
1449          }
1450  
1451          private void NotifyLanguageChanged()
1452          {
1453              OutGoingLanguageSettings outsettings = new OutGoingLanguageSettings(Languages[_languagesIndex].Tag);
1454  
1455              SendConfigMSG(outsettings.ToString());
1456          }
1457  
1458          internal void RefreshSettingsOnExternalChange()
1459          {
1460              EnableDataDiagnostics = DataDiagnosticsSettings.GetEnabledValue();
1461  
1462              NotifyPropertyChanged(nameof(EnableDataDiagnostics));
1463          }
1464  
1465          // Per retention policy
1466          private void DeleteDiagnosticDataOlderThan28Days(string etwDirPath)
1467          {
1468              if (!Directory.Exists(etwDirPath))
1469              {
1470                  return;
1471              }
1472  
1473              var directoryInfo = new DirectoryInfo(etwDirPath);
1474              var cutoffDate = DateTime.Now.AddDays(-28);
1475  
1476              foreach (var file in directoryInfo.GetFiles())
1477              {
1478                  if (file.LastWriteTime < cutoffDate)
1479                  {
1480                      try
1481                      {
1482                          file.Delete();
1483                      }
1484                      catch (Exception ex)
1485                      {
1486                          Logger.LogError($"Failed to delete file: {file.FullName}. Error: {ex.Message}");
1487                      }
1488                  }
1489              }
1490          }
1491  
1492          internal void ViewDiagnosticData()
1493          {
1494              string localLowEtwDirPath = Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), "AppData", "LocalLow", "Microsoft", "PowerToys", "etw");
1495              string etwDirPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\etw");
1496  
1497              if (Directory.Exists(localLowEtwDirPath))
1498              {
1499                  if (!Directory.Exists(etwDirPath))
1500                  {
1501                      Directory.CreateDirectory(etwDirPath);
1502                  }
1503  
1504                  string[] localLowEtlFiles = Directory.GetFiles(localLowEtwDirPath, "*.etl");
1505  
1506                  foreach (string file in localLowEtlFiles)
1507                  {
1508                      string fileName = Path.GetFileName(file);
1509                      string destFile = Path.Combine(etwDirPath, fileName);
1510  
1511                      try
1512                      {
1513                          File.Copy(file, destFile, overwrite: true);
1514                      }
1515                      catch (Exception ex)
1516                      {
1517                          Logger.LogError($"Failed to copy etl file: {fileName}. Error: {ex.Message}");
1518                      }
1519                  }
1520              }
1521  
1522              string tracerptPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "system32");
1523  
1524              ETLConverter converter = new ETLConverter(etwDirPath, tracerptPath);
1525              Task.Run(() => converter.ConvertDiagnosticsETLsAsync()).Wait();
1526  
1527              if (Directory.Exists(etwDirPath))
1528              {
1529                  // Open etw dir in FileExplorer
1530                  Process.Start("explorer.exe", etwDirPath);
1531              }
1532          }
1533  
1534          private void OnSettingsChanged(GeneralSettings newSettings)
1535          {
1536              _dispatcherQueue?.TryEnqueue(() =>
1537              {
1538                  GeneralSettingsConfig = newSettings;
1539  
1540                  if (_enableQuickAccess != newSettings.EnableQuickAccess)
1541                  {
1542                      _enableQuickAccess = newSettings.EnableQuickAccess;
1543                      OnPropertyChanged(nameof(EnableQuickAccess));
1544                  }
1545              });
1546          }
1547  
1548          public override void Dispose()
1549          {
1550              base.Dispose();
1551              if (_settingsRepository != null)
1552              {
1553                  _settingsRepository.SettingsChanged -= OnSettingsChanged;
1554              }
1555  
1556              GC.SuppressFinalize(this);
1557          }
1558  
1559          protected virtual Microsoft.UI.Dispatching.DispatcherQueue GetDispatcherQueue()
1560          {
1561              return Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
1562          }
1563      }
1564  }