mirror of
https://github.com/ElectronNET/Electron.NET.git
synced 2026-02-09 21:24:55 +00:00
509 lines
19 KiB
C#
509 lines
19 KiB
C#
using ElectronNET.API.Entities;
|
||
using ElectronNET.API.Serialization;
|
||
using ElectronNET.Common;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Text.Json;
|
||
using System.Threading.Tasks;
|
||
|
||
// ReSharper disable InconsistentNaming
|
||
|
||
namespace ElectronNET.API
|
||
{
|
||
/// <summary>
|
||
/// Enable apps to automatically update themselves. Based on electron-updater.
|
||
/// </summary>
|
||
public sealed class AutoUpdater
|
||
{
|
||
/// <summary>
|
||
/// Whether to automatically download an update when it is found. (Default is true)
|
||
/// </summary>
|
||
public bool AutoDownload
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||
|
||
BridgeConnector.Socket.On<bool>("autoUpdater-autoDownload-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-autoDownload-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdater-autoDownload-get");
|
||
|
||
return taskCompletionSource.Task;
|
||
}).Result;
|
||
}
|
||
set
|
||
{
|
||
BridgeConnector.Socket.Emit("autoUpdater-autoDownload-set", value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Whether to automatically install a downloaded update on app quit (if `QuitAndInstall` was not called before).
|
||
///
|
||
/// Applicable only on Windows and Linux.
|
||
/// </summary>
|
||
public bool AutoInstallOnAppQuit
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||
|
||
BridgeConnector.Socket.On<bool>("autoUpdater-autoInstallOnAppQuit-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-autoInstallOnAppQuit-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdater-autoInstallOnAppQuit-get");
|
||
|
||
return taskCompletionSource.Task;
|
||
}).Result;
|
||
}
|
||
set
|
||
{
|
||
BridgeConnector.Socket.Emit("autoUpdater-autoInstallOnAppQuit-set", value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// *GitHub provider only.* Whether to allow update to pre-release versions.
|
||
/// Defaults to "true" if application version contains prerelease components (e.g. "0.12.1-alpha.1", here "alpha" is a prerelease component), otherwise "false".
|
||
///
|
||
/// If "true", downgrade will be allowed("allowDowngrade" will be set to "true").
|
||
/// </summary>
|
||
public bool AllowPrerelease
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||
|
||
BridgeConnector.Socket.On<bool>("autoUpdater-allowPrerelease-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-allowPrerelease-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdater-allowPrerelease-get");
|
||
|
||
return taskCompletionSource.Task;
|
||
}).Result;
|
||
}
|
||
set
|
||
{
|
||
BridgeConnector.Socket.Emit("autoUpdater-allowPrerelease-set", value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// *GitHub provider only.*
|
||
/// Get all release notes (from current version to latest), not just the latest (Default is false).
|
||
/// </summary>
|
||
public bool FullChangelog
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||
|
||
BridgeConnector.Socket.On<bool>("autoUpdater-fullChangelog-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-fullChangelog-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdater-fullChangelog-get");
|
||
|
||
return taskCompletionSource.Task;
|
||
}).Result;
|
||
}
|
||
set
|
||
{
|
||
BridgeConnector.Socket.Emit("autoUpdater-fullChangelog-set", value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Whether to allow version downgrade (when a user from the beta channel wants to go back to the stable channel).
|
||
/// Taken in account only if channel differs (pre-release version component in terms of semantic versioning).
|
||
/// Default is false.
|
||
/// </summary>
|
||
public bool AllowDowngrade
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||
|
||
BridgeConnector.Socket.On<bool>("autoUpdater-allowDowngrade-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-allowDowngrade-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdater-allowDowngrade-get");
|
||
|
||
return taskCompletionSource.Task;
|
||
}).Result;
|
||
}
|
||
set
|
||
{
|
||
BridgeConnector.Socket.Emit("autoUpdater-allowDowngrade-set", value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// For test only.
|
||
/// </summary>
|
||
public string UpdateConfigPath
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<string>();
|
||
|
||
BridgeConnector.Socket.On<string>("autoUpdater-updateConfigPath-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-updateConfigPath-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdater-updateConfigPath-get");
|
||
|
||
return taskCompletionSource.Task;
|
||
}).Result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// The current application version
|
||
/// </summary>
|
||
public Task<SemVer> CurrentVersionAsync
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<SemVer>();
|
||
|
||
BridgeConnector.Socket.On<SemVer>("autoUpdater-currentVersion-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-currentVersion-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
BridgeConnector.Socket.Emit("autoUpdater-currentVersion-get");
|
||
|
||
return taskCompletionSource.Task;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get the update channel. Not applicable for GitHub.
|
||
/// Doesn’t return channel from the update configuration, only if was previously set.
|
||
/// </summary>
|
||
[Obsolete("Use the asynchronous version ChannelAsync instead")]
|
||
public string Channel
|
||
{
|
||
get
|
||
{
|
||
return ChannelAsync.Result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get the update channel. Not applicable for GitHub.
|
||
/// Doesn’t return channel from the update configuration, only if was previously set.
|
||
/// </summary>
|
||
public Task<string> ChannelAsync
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<string>();
|
||
|
||
BridgeConnector.Socket.On<string>("autoUpdater-channel-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-channel-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
BridgeConnector.Socket.Emit("autoUpdater-channel-get");
|
||
|
||
return taskCompletionSource.Task;
|
||
});
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// The request headers.
|
||
/// </summary>
|
||
public Task<Dictionary<string, string>> RequestHeadersAsync
|
||
{
|
||
get
|
||
{
|
||
return Task.Run(() =>
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<Dictionary<string, string>>();
|
||
BridgeConnector.Socket.On<Dictionary<string, string>>("autoUpdater-requestHeaders-get-reply", (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdater-requestHeaders-get-reply");
|
||
taskCompletionSource.SetResult(result);
|
||
});
|
||
BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-get");
|
||
return taskCompletionSource.Task;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// The request headers.
|
||
/// </summary>
|
||
public Dictionary<string, string> RequestHeaders
|
||
{
|
||
set
|
||
{
|
||
BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-set", value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Emitted when there is an error while updating.
|
||
/// </summary>
|
||
public event Action<string> OnError
|
||
{
|
||
add => ApiEventManager.AddEvent("autoUpdater-error", GetHashCode(), _error, value, (args) => args.ToString());
|
||
remove => ApiEventManager.RemoveEvent("autoUpdater-error", GetHashCode(), _error, value);
|
||
}
|
||
|
||
private event Action<string> _error;
|
||
|
||
/// <summary>
|
||
/// Emitted when checking if an update has started.
|
||
/// </summary>
|
||
public event Action OnCheckingForUpdate
|
||
{
|
||
add => ApiEventManager.AddEvent("autoUpdater-checking-for-update", GetHashCode(), _checkingForUpdate, value);
|
||
remove => ApiEventManager.RemoveEvent("autoUpdater-checking-for-update", GetHashCode(), _checkingForUpdate, value);
|
||
}
|
||
|
||
private event Action _checkingForUpdate;
|
||
|
||
/// <summary>
|
||
/// Emitted when there is an available update.
|
||
/// The update is downloaded automatically if AutoDownload is true.
|
||
/// </summary>
|
||
public event Action<UpdateInfo> OnUpdateAvailable
|
||
{
|
||
add => ApiEventManager.AddEvent("autoUpdater-update-available", GetHashCode(), _updateAvailable, value, (args) => args.Deserialize(ElectronJsonContext.Default.UpdateInfo));
|
||
remove => ApiEventManager.RemoveEvent("autoUpdater-update-available", GetHashCode(), _updateAvailable, value);
|
||
}
|
||
|
||
private event Action<UpdateInfo> _updateAvailable;
|
||
|
||
/// <summary>
|
||
/// Emitted when there is no available update.
|
||
/// </summary>
|
||
public event Action<UpdateInfo> OnUpdateNotAvailable
|
||
{
|
||
add => ApiEventManager.AddEvent("autoUpdater-update-not-available", GetHashCode(), _updateNotAvailable, value, (args) => args.Deserialize(ElectronJsonContext.Default.UpdateInfo));
|
||
remove => ApiEventManager.RemoveEvent("autoUpdater-update-not-available", GetHashCode(), _updateNotAvailable, value);
|
||
}
|
||
|
||
private event Action<UpdateInfo> _updateNotAvailable;
|
||
|
||
/// <summary>
|
||
/// Emitted on download progress.
|
||
/// </summary>
|
||
public event Action<ProgressInfo> OnDownloadProgress
|
||
{
|
||
add => ApiEventManager.AddEvent("autoUpdater-download-progress", GetHashCode(), _downloadProgress, value, (args) => args.Deserialize(ElectronJsonContext.Default.ProgressInfo));
|
||
remove => ApiEventManager.RemoveEvent("autoUpdater-download-progress", GetHashCode(), _downloadProgress, value);
|
||
}
|
||
|
||
private event Action<ProgressInfo> _downloadProgress;
|
||
|
||
/// <summary>
|
||
/// Emitted on download complete.
|
||
/// </summary>
|
||
public event Action<UpdateInfo> OnUpdateDownloaded
|
||
{
|
||
add => ApiEventManager.AddEvent("autoUpdater-update-downloaded", GetHashCode(), _updateDownloaded, value, (args) => args.Deserialize(ElectronJsonContext.Default.UpdateInfo));
|
||
remove => ApiEventManager.RemoveEvent("autoUpdater-update-downloaded", GetHashCode(), _updateDownloaded, value);
|
||
}
|
||
|
||
private event Action<UpdateInfo> _updateDownloaded;
|
||
|
||
private static AutoUpdater _autoUpdater;
|
||
private static object _syncRoot = new object();
|
||
|
||
internal AutoUpdater()
|
||
{
|
||
}
|
||
|
||
internal static AutoUpdater Instance
|
||
{
|
||
get
|
||
{
|
||
if (_autoUpdater == null)
|
||
{
|
||
lock (_syncRoot)
|
||
{
|
||
if (_autoUpdater == null)
|
||
{
|
||
_autoUpdater = new AutoUpdater();
|
||
}
|
||
}
|
||
}
|
||
|
||
return _autoUpdater;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Asks the server whether there is an update.
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public Task<UpdateCheckResult> CheckForUpdatesAsync()
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>();
|
||
string guid = Guid.NewGuid().ToString();
|
||
|
||
BridgeConnector.Socket.On<UpdateCheckResult>("autoUpdaterCheckForUpdatesComplete" + guid, (result) =>
|
||
{
|
||
try
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid);
|
||
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid);
|
||
taskCompletionSource.SetResult(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
taskCompletionSource.SetException(ex);
|
||
}
|
||
});
|
||
BridgeConnector.Socket.On<string>("autoUpdaterCheckForUpdatesError" + guid, (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid);
|
||
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid);
|
||
string message = "An error occurred in CheckForUpdatesAsync";
|
||
if (!string.IsNullOrEmpty(result)) message = result;
|
||
taskCompletionSource.SetException(new Exception(message));
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdates", guid);
|
||
|
||
return taskCompletionSource.Task;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Asks the server whether there is an update.
|
||
///
|
||
/// This will immediately download an update, then install when the app quits.
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public Task<UpdateCheckResult> CheckForUpdatesAndNotifyAsync()
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>();
|
||
string guid = Guid.NewGuid().ToString();
|
||
|
||
BridgeConnector.Socket.On<UpdateCheckResult>("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid, (result) =>
|
||
{
|
||
try
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
|
||
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
|
||
taskCompletionSource.SetResult(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
taskCompletionSource.SetException(ex);
|
||
}
|
||
});
|
||
BridgeConnector.Socket.On<string>("autoUpdaterCheckForUpdatesAndNotifyError" + guid, (result) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
|
||
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
|
||
string message = "An error occurred in autoUpdaterCheckForUpdatesAndNotify";
|
||
if (!string.IsNullOrEmpty(result)) message = result;
|
||
taskCompletionSource.SetException(new Exception(message));
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdatesAndNotify", guid);
|
||
|
||
return taskCompletionSource.Task;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Restarts the app and installs the update after it has been downloaded.
|
||
/// It should only be called after `update-downloaded` has been emitted.
|
||
///
|
||
/// Note: QuitAndInstall() will close all application windows first and only emit `before-quit` event on `app` after that.
|
||
/// This is different from the normal quit event sequence.
|
||
/// </summary>
|
||
/// <param name="isSilent">*windows-only* Runs the installer in silent mode. Defaults to `false`.</param>
|
||
/// <param name="isForceRunAfter">Run the app after finish even on silent install. Not applicable for macOS. Ignored if `isSilent` is set to `false`.</param>
|
||
public void QuitAndInstall(bool isSilent = false, bool isForceRunAfter = false)
|
||
{
|
||
BridgeConnector.Socket.Emit("autoUpdaterQuitAndInstall", isSilent, isForceRunAfter);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Start downloading update manually. You can use this method if "AutoDownload" option is set to "false".
|
||
/// </summary>
|
||
/// <returns>Path to downloaded file.</returns>
|
||
public Task<string> DownloadUpdateAsync()
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<string>();
|
||
string guid = Guid.NewGuid().ToString();
|
||
|
||
BridgeConnector.Socket.On<string>("autoUpdaterDownloadUpdateComplete" + guid, (downloadedPath) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdaterDownloadUpdateComplete" + guid);
|
||
taskCompletionSource.SetResult(downloadedPath);
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdaterDownloadUpdate", guid);
|
||
|
||
return taskCompletionSource.Task;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Feed URL.
|
||
/// </summary>
|
||
/// <returns>Feed URL.</returns>
|
||
public Task<string> GetFeedURLAsync()
|
||
{
|
||
var taskCompletionSource = new TaskCompletionSource<string>();
|
||
string guid = Guid.NewGuid().ToString();
|
||
|
||
BridgeConnector.Socket.On<string>("autoUpdaterGetFeedURLComplete" + guid, (downloadedPath) =>
|
||
{
|
||
BridgeConnector.Socket.Off("autoUpdaterGetFeedURLComplete" + guid);
|
||
taskCompletionSource.SetResult(downloadedPath);
|
||
});
|
||
|
||
BridgeConnector.Socket.Emit("autoUpdaterGetFeedURL", guid);
|
||
|
||
return taskCompletionSource.Task;
|
||
}
|
||
|
||
|
||
}
|
||
}
|
||
|
||
|