UpdateViewModel.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. using System;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Reflection;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using PixiEditor.Helpers;
  9. using PixiEditor.Models.Dialogs;
  10. using PixiEditor.Models.Processes;
  11. using PixiEditor.Models.UserPreferences;
  12. using PixiEditor.UpdateModule;
  13. namespace PixiEditor.ViewModels.SubViewModels.Main
  14. {
  15. public class UpdateViewModel : SubViewModel<ViewModelMain>
  16. {
  17. private bool updateReadyToInstall = false;
  18. public UpdateChecker UpdateChecker { get; set; }
  19. public RelayCommand RestartApplicationCommand { get; set; }
  20. private string versionText;
  21. public string VersionText
  22. {
  23. get => versionText;
  24. set
  25. {
  26. versionText = value;
  27. RaisePropertyChanged(nameof(VersionText));
  28. }
  29. }
  30. public bool UpdateReadyToInstall
  31. {
  32. get => updateReadyToInstall;
  33. set
  34. {
  35. updateReadyToInstall = value;
  36. RaisePropertyChanged(nameof(UpdateReadyToInstall));
  37. if (value)
  38. {
  39. VersionText = $"to install update (current {UpdateChecker.CurrentVersionTag})"; // Button shows "Restart" before this text
  40. }
  41. }
  42. }
  43. public UpdateViewModel(ViewModelMain owner)
  44. : base(owner)
  45. {
  46. Owner.OnStartupEvent += Owner_OnStartupEvent;
  47. RestartApplicationCommand = new RelayCommand(RestartApplication);
  48. InitUpdateChecker();
  49. }
  50. public async Task<bool> CheckForUpdate()
  51. {
  52. bool updateAvailable = await UpdateChecker.CheckUpdateAvailable();
  53. bool updateCompatible = await UpdateChecker.IsUpdateCompatible();
  54. bool updateFileDoesNotExists = !File.Exists(
  55. Path.Join(UpdateDownloader.DownloadLocation, $"update-{UpdateChecker.LatestReleaseInfo.TagName}.zip"));
  56. bool updateExeDoesNotExists = !File.Exists(
  57. Path.Join(UpdateDownloader.DownloadLocation, $"update-{UpdateChecker.LatestReleaseInfo.TagName}.exe"));
  58. if (updateAvailable && updateFileDoesNotExists && updateExeDoesNotExists)
  59. {
  60. VersionText = "Downloading update...";
  61. if (updateCompatible)
  62. {
  63. await UpdateDownloader.DownloadReleaseZip(UpdateChecker.LatestReleaseInfo);
  64. }
  65. else
  66. {
  67. await UpdateDownloader.DownloadInstaller(UpdateChecker.LatestReleaseInfo);
  68. }
  69. UpdateReadyToInstall = true;
  70. return true;
  71. }
  72. return false;
  73. }
  74. private static void AskToInstall()
  75. {
  76. string dir = AppDomain.CurrentDomain.BaseDirectory;
  77. UpdateDownloader.CreateTempDirectory();
  78. bool updateZipExists = Directory.GetFiles(UpdateDownloader.DownloadLocation, "update-*.zip").Length > 0;
  79. string[] updateExeFiles = Directory.GetFiles(UpdateDownloader.DownloadLocation, "update-*.exe");
  80. bool updateExeExists = updateExeFiles.Length > 0;
  81. string updaterPath = Path.Join(dir, "PixiEditor.UpdateInstaller.exe");
  82. if (updateZipExists || updateExeExists)
  83. {
  84. ViewModelMain.Current.UpdateSubViewModel.UpdateReadyToInstall = true;
  85. var result = ConfirmationDialog.Show("Update is ready to install. Do you want to install it now?");
  86. if (result == Models.Enums.ConfirmationType.Yes)
  87. {
  88. if (updateZipExists && File.Exists(updaterPath))
  89. {
  90. InstallHeadless(updaterPath);
  91. }
  92. else if (updateExeExists)
  93. {
  94. OpenExeInstaller(updateExeFiles[0]);
  95. }
  96. }
  97. }
  98. }
  99. private static void InstallHeadless(string updaterPath)
  100. {
  101. try
  102. {
  103. ProcessHelper.RunAsAdmin(updaterPath);
  104. Application.Current.Shutdown();
  105. }
  106. catch (Win32Exception)
  107. {
  108. MessageBox.Show(
  109. "Couldn't update without administrator rights.",
  110. "Insufficient permissions",
  111. MessageBoxButton.OK,
  112. MessageBoxImage.Error);
  113. }
  114. }
  115. private static void OpenExeInstaller(string updateExeFile)
  116. {
  117. bool alreadyUpdated = AssemblyHelper.GetCurrentAssemblyVersion() ==
  118. updateExeFile.Split('-')[1].Split(".exe")[0];
  119. if (!alreadyUpdated)
  120. {
  121. RestartToUpdate(updateExeFile);
  122. }
  123. else
  124. {
  125. File.Delete(updateExeFile);
  126. }
  127. }
  128. private static void RestartToUpdate(string updateExeFile)
  129. {
  130. Process.Start(updateExeFile);
  131. Application.Current.Shutdown();
  132. }
  133. private static void RestartApplication(object parameter)
  134. {
  135. try
  136. {
  137. ProcessHelper.RunAsAdmin(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "PixiEditor.UpdateInstaller.exe"));
  138. Application.Current.Shutdown();
  139. }
  140. catch (Win32Exception)
  141. {
  142. NoticeDialog.Show("Couldn't update without administrator rights.", "Insufficient permissions");
  143. }
  144. }
  145. private async void Owner_OnStartupEvent(object sender, EventArgs e)
  146. {
  147. if (IPreferences.Current.GetPreference("CheckUpdatesOnStartup", true))
  148. {
  149. try
  150. {
  151. await CheckForUpdate();
  152. }
  153. catch (System.Net.Http.HttpRequestException)
  154. {
  155. NoticeDialog.Show("Could not check if there's an update available");
  156. }
  157. AskToInstall();
  158. }
  159. }
  160. private void InitUpdateChecker()
  161. {
  162. string version = AssemblyHelper.GetCurrentAssemblyVersion();
  163. UpdateChecker = new UpdateChecker(version);
  164. VersionText = $"Version {version}";
  165. }
  166. }
  167. }