using ElectronNET.API.Entities; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace ElectronNET.API { /// /// Enable apps to automatically update themselves. Based on electron-updater. /// public sealed class AutoUpdater { /// /// Whether to automatically download an update when it is found. (Default is true) /// public Task IsAutoDownloadEnabledAsync() => BridgeConnector.OnResult("autoUpdater-autoDownload-get", "autoUpdater-autoDownload-get-reply"); /// /// Whether to automatically install a downloaded update on app quit (if `QuitAndInstall` was not called before). /// /// Applicable only on Windows and Linux. /// public Task IsAutoInstallOnAppQuitEnabledAsync() => BridgeConnector.OnResult("autoUpdater-autoInstallOnAppQuit-get", "autoUpdater-autoInstallOnAppQuit-get-reply"); /// /// *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"). /// public Task IsAllowPrereleaseEnabledAsync() => BridgeConnector.OnResult("autoUpdater-allowPrerelease-get", "autoUpdater-allowPrerelease-get-reply"); /// /// *GitHub provider only.* /// Get all release notes (from current version to latest), not just the latest (Default is false). /// public Task IsFullChangeLogEnabledAsync() => BridgeConnector.OnResult("autoUpdater-fullChangelog-get", "autoUpdater-fullChangelog-get-reply"); public Task IsAllowDowngradeEnabledAsync() => BridgeConnector.OnResult("autoUpdater-allowDowngrade-get", "autoUpdater-allowDowngrade-get-reply"); /// /// Whether to automatically download an update when it is found. (Default is true) /// public bool AutoDownload { set { BridgeConnector.Emit("autoUpdater-autoDownload-set", value); } } /// /// Whether to automatically install a downloaded update on app quit (if `QuitAndInstall` was not called before). /// /// Applicable only on Windows and Linux. /// public bool AutoInstallOnAppQuit { set { BridgeConnector.Emit("autoUpdater-autoInstallOnAppQuit-set", value); } } /// /// *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"). /// public bool AllowPrerelease { set { BridgeConnector.Emit("autoUpdater-allowPrerelease-set", value); } } /// /// *GitHub provider only.* /// Get all release notes (from current version to latest), not just the latest (Default is false). /// public bool FullChangelog { set { BridgeConnector.Emit("autoUpdater-fullChangelog-set", value); } } /// /// 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. /// public bool AllowDowngrade { set { BridgeConnector.Emit("autoUpdater-allowDowngrade-set", value); } } /// /// For test only. /// public Task GetUpdateConfigPathAsync() => BridgeConnector.OnResult("autoUpdater-updateConfigPath-get", "autoUpdater-updateConfigPath-get-reply"); /// /// The current application version /// public Task GetCurrentVersionAsync() => BridgeConnector.OnResult("autoUpdater-updateConcurrentVersionfigPath-get", "autoUpdater-currentVersion-get-reply"); /// /// Get the update channel. Not applicable for GitHub. /// Doesn’t return channel from the update configuration, only if was previously set. /// public Task GetChannelAsync() => BridgeConnector.OnResult("autoUpdater-channel-get", "autoUpdater-channel-get-reply"); /// /// The request headers. /// public Task> GetRequestHeadersAsync() => BridgeConnector.OnResult>("autoUpdater-requestHeaders-get", "autoUpdater-requestHeaders-get-reply"); /// /// The request headers. /// public Dictionary RequestHeaders { set { BridgeConnector.Emit("autoUpdater-requestHeaders-set", value); } } /// /// Emitted when there is an error while updating. /// public event Action OnError { add { if (_error == null) { BridgeConnector.On("autoUpdater-error" + GetHashCode(), (message) => { _error(message.ToString()); }); BridgeConnector.Emit("register-autoUpdater-error-event", GetHashCode()); } _error += value; } remove { _error -= value; if (_error == null) BridgeConnector.Off("autoUpdater-error" + GetHashCode()); } } private event Action _error; /// /// Emitted when checking if an update has started. /// public event Action OnCheckingForUpdate { add { if (_checkingForUpdate == null) { BridgeConnector.On("autoUpdater-checking-for-update" + GetHashCode(), () => { _checkingForUpdate(); }); BridgeConnector.Emit("register-autoUpdater-checking-for-update-event", GetHashCode()); } _checkingForUpdate += value; } remove { _checkingForUpdate -= value; if (_checkingForUpdate == null) BridgeConnector.Off("autoUpdater-checking-for-update" + GetHashCode()); } } private event Action _checkingForUpdate; /// /// Emitted when there is an available update. /// The update is downloaded automatically if AutoDownload is true. /// public event Action OnUpdateAvailable { add { if (_updateAvailable == null) { BridgeConnector.On("autoUpdater-update-available" + GetHashCode(), (updateInfo) => { _updateAvailable(updateInfo); }); BridgeConnector.Emit("register-autoUpdater-update-available-event", GetHashCode()); } _updateAvailable += value; } remove { _updateAvailable -= value; if (_updateAvailable == null) BridgeConnector.Off("autoUpdater-update-available" + GetHashCode()); } } private event Action _updateAvailable; /// /// Emitted when there is no available update. /// public event Action OnUpdateNotAvailable { add { if (_updateNotAvailable == null) { BridgeConnector.On("autoUpdater-update-not-available" + GetHashCode(), (updateInfo) => { _updateNotAvailable(updateInfo); }); BridgeConnector.Emit("register-autoUpdater-update-not-available-event", GetHashCode()); } _updateNotAvailable += value; } remove { _updateNotAvailable -= value; if (_updateNotAvailable == null) BridgeConnector.Off("autoUpdater-update-not-available" + GetHashCode()); } } private event Action _updateNotAvailable; /// /// Emitted on download progress. /// public event Action OnDownloadProgress { add { if (_downloadProgress == null) { BridgeConnector.On("autoUpdater-download-progress" + GetHashCode(), (progressInfo) => { _downloadProgress(progressInfo); }); BridgeConnector.Emit("register-autoUpdater-download-progress-event", GetHashCode()); } _downloadProgress += value; } remove { _downloadProgress -= value; if (_downloadProgress == null) BridgeConnector.Off("autoUpdater-download-progress" + GetHashCode()); } } private event Action _downloadProgress; /// /// Emitted on download complete. /// public event Action OnUpdateDownloaded { add { if (_updateDownloaded == null) { BridgeConnector.On("autoUpdater-update-downloaded" + GetHashCode(), (updateInfo) => { _updateDownloaded(updateInfo); }); BridgeConnector.Emit("register-autoUpdater-update-downloaded-event", GetHashCode()); } _updateDownloaded += value; } remove { _updateDownloaded -= value; if (_updateDownloaded == null) BridgeConnector.Off("autoUpdater-update-downloaded" + GetHashCode()); } } private event Action _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; } } /// /// Asks the server whether there is an update. /// /// public Task CheckForUpdatesAsync() { var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); string guid = Guid.NewGuid().ToString(); BridgeConnector.On("autoUpdaterCheckForUpdatesComplete" + guid, (updateCheckResult) => { try { BridgeConnector.Off("autoUpdaterCheckForUpdatesComplete" + guid); BridgeConnector.Off("autoUpdaterCheckForUpdatesError" + guid); taskCompletionSource.SetResult(updateCheckResult); } catch (Exception ex) { taskCompletionSource.SetException(ex); } }); BridgeConnector.On("autoUpdaterCheckForUpdatesError" + guid, (error) => { BridgeConnector.Off("autoUpdaterCheckForUpdatesComplete" + guid); BridgeConnector.Off("autoUpdaterCheckForUpdatesError" + guid); string message = "An error occurred in CheckForUpdatesAsync"; if (error != null && !string.IsNullOrEmpty(error.ToString())) message = JsonConvert.SerializeObject(error); taskCompletionSource.SetException(new Exception(message)); }); BridgeConnector.Emit("autoUpdaterCheckForUpdates", guid); return taskCompletionSource.Task; } /// /// Asks the server whether there is an update. /// /// This will immediately download an update, then install when the app quits. /// /// public Task CheckForUpdatesAndNotifyAsync() { var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); string guid = Guid.NewGuid().ToString(); BridgeConnector.On("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid, (updateCheckResult) => { try { BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid); BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid); if (updateCheckResult == null) taskCompletionSource.SetResult(null); else taskCompletionSource.SetResult(updateCheckResult); } catch (Exception ex) { taskCompletionSource.SetException(ex); } }); BridgeConnector.On("autoUpdaterCheckForUpdatesAndNotifyError" + guid, (error) => { BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid); BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid); string message = "An error occurred in autoUpdaterCheckForUpdatesAndNotify"; if (error != null) message = JsonConvert.SerializeObject(error); taskCompletionSource.SetException(new Exception(message)); }); BridgeConnector.Emit("autoUpdaterCheckForUpdatesAndNotify", guid); return taskCompletionSource.Task; } /// /// 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. /// /// *windows-only* Runs the installer in silent mode. Defaults to `false`. /// Run the app after finish even on silent install. Not applicable for macOS. Ignored if `isSilent` is set to `false`. public void QuitAndInstall(bool isSilent = false, bool isForceRunAfter = false) { BridgeConnector.EmitSync("autoUpdaterQuitAndInstall", isSilent, isForceRunAfter); } /// /// Start downloading update manually. You can use this method if "AutoDownload" option is set to "false". /// /// Path to downloaded file. public Task DownloadUpdateAsync() { var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); string guid = Guid.NewGuid().ToString(); BridgeConnector.On("autoUpdaterDownloadUpdateComplete" + guid, (downloadedPath) => { BridgeConnector.Off("autoUpdaterDownloadUpdateComplete" + guid); taskCompletionSource.SetResult(downloadedPath.ToString()); }); BridgeConnector.Emit("autoUpdaterDownloadUpdate", guid); return taskCompletionSource.Task; } /// /// Feed URL. /// /// Feed URL. public Task GetFeedURLAsync() { var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); string guid = Guid.NewGuid().ToString(); BridgeConnector.On("autoUpdaterGetFeedURLComplete" + guid, (downloadedPath) => { BridgeConnector.Off("autoUpdaterGetFeedURLComplete" + guid); taskCompletionSource.SetResult(downloadedPath.ToString()); }); BridgeConnector.Emit("autoUpdaterGetFeedURL", guid); return taskCompletionSource.Task; } } }