2019-05-20 01:08:26 +02:00
using ElectronNET.API.Entities ;
2020-07-08 11:16:00 +02:00
using Newtonsoft.Json ;
2019-05-20 01:08:26 +02:00
using Newtonsoft.Json.Linq ;
2020-07-08 11:16:00 +02:00
using Newtonsoft.Json.Serialization ;
2019-05-20 01:08:26 +02:00
using System ;
2020-07-08 11:16:00 +02:00
using System.Collections.Generic ;
2019-05-20 01:08:26 +02:00
using System.Threading.Tasks ;
namespace ElectronNET.API
{
/// <summary>
/// Enable apps to automatically update themselves. Based on electron-updater.
/// </summary>
public sealed class AutoUpdater
{
2019-05-23 23:10:45 +02:00
/// <summary>
/// Whether to automatically download an update when it is found. (Default is true)
/// </summary>
2021-08-23 11:22:37 +02:00
public Task < bool > IsAutoDownloadEnabledAsync ( ) = > BridgeConnector . OnResult < bool > ( "autoUpdater-autoDownload-get" , "autoUpdater-autoDownload-get-reply" ) ;
2019-05-23 23:10:45 +02:00
2021-07-12 18:16:07 +02:00
/// <summary>
/// Whether to automatically install a downloaded update on app quit (if `QuitAndInstall` was not called before).
///
/// Applicable only on Windows and Linux.
/// </summary>
2021-08-23 11:22:37 +02:00
public Task < bool > IsAutoInstallOnAppQuitEnabledAsync ( ) = > BridgeConnector . OnResult < bool > ( "autoUpdater-autoInstallOnAppQuit-get" , "autoUpdater-autoInstallOnAppQuit-get-reply" ) ;
2021-07-12 18:16:07 +02:00
/// <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>
2021-08-23 11:22:37 +02:00
public Task < bool > IsAllowPrereleaseEnabledAsync ( ) = > BridgeConnector . OnResult < bool > ( "autoUpdater-allowPrerelease-get" , "autoUpdater-allowPrerelease-get-reply" ) ;
2021-07-12 18:16:07 +02:00
/// <summary>
/// *GitHub provider only.*
/// Get all release notes (from current version to latest), not just the latest (Default is false).
/// </summary>
2021-08-23 11:22:37 +02:00
public Task < bool > IsFullChangeLogEnabledAsync ( ) = > BridgeConnector . OnResult < bool > ( "autoUpdater-fullChangelog-get" , "autoUpdater-fullChangelog-get-reply" ) ;
2021-07-12 18:16:07 +02:00
2021-08-23 11:22:37 +02:00
public Task < bool > IsAllowDowngradeEnabledAsync ( ) = > BridgeConnector . OnResult < bool > ( "autoUpdater-allowDowngrade-get" , "autoUpdater-allowDowngrade-get-reply" ) ;
2021-07-12 18:16:07 +02:00
/// <summary>
/// Whether to automatically download an update when it is found. (Default is true)
/// </summary>
public bool AutoDownload
{
2019-05-23 23:10:45 +02:00
set
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdater-autoDownload-set" , value ) ;
2019-05-23 23:10:45 +02:00
}
}
/// <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
{
set
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdater-autoInstallOnAppQuit-set" , value ) ;
2019-05-23 23:10:45 +02:00
}
}
/// <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
{
set
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdater-allowPrerelease-set" , value ) ;
2019-05-23 23:10:45 +02:00
}
}
/// <summary>
/// *GitHub provider only.*
/// Get all release notes (from current version to latest), not just the latest (Default is false).
/// </summary>
public bool FullChangelog
{
set
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdater-fullChangelog-set" , value ) ;
2019-05-23 23:10:45 +02:00
}
}
/// <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
{
set
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdater-allowDowngrade-set" , value ) ;
2019-05-23 23:10:45 +02:00
}
}
/// <summary>
/// For test only.
/// </summary>
2021-08-23 11:22:37 +02:00
public Task < string > GetUpdateConfigPathAsync ( ) = > BridgeConnector . OnResult < string > ( "autoUpdater-updateConfigPath-get" , "autoUpdater-updateConfigPath-get-reply" ) ;
2019-05-23 23:10:45 +02:00
2020-07-08 11:16:00 +02:00
/// <summary>
/// The current application version
/// </summary>
2021-08-23 11:22:37 +02:00
public Task < SemVer > GetCurrentVersionAsync ( ) = > BridgeConnector . OnResult < SemVer > ( "autoUpdater-updateConcurrentVersionfigPath-get" , "autoUpdater-currentVersion-get-reply" ) ;
2020-07-08 11:16:00 +02:00
2019-05-23 23:10:45 +02:00
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Doesn’ t return channel from the update configuration, only if was previously set.
/// </summary>
2021-08-23 11:22:37 +02:00
public Task < string > GetChannelAsync ( ) = > BridgeConnector . OnResult < string > ( "autoUpdater-channel-get" , "autoUpdater-channel-get-reply" ) ;
2020-07-08 11:16:00 +02:00
/// <summary>
/// The request headers.
/// </summary>
2021-08-23 11:22:37 +02:00
public Task < Dictionary < string , string > > GetRequestHeadersAsync ( ) = > BridgeConnector . OnResult < Dictionary < string , string > > ( "autoUpdater-requestHeaders-get" , "autoUpdater-requestHeaders-get-reply" ) ;
2019-05-23 23:10:45 +02:00
2019-05-23 02:59:30 +02:00
/// <summary>
2020-07-08 11:16:00 +02:00
/// The request headers.
2019-05-23 02:59:30 +02:00
/// </summary>
2020-07-08 11:16:00 +02:00
public Dictionary < string , string > RequestHeaders
{
set
{
2021-08-23 11:57:42 +02:00
BridgeConnector . Emit ( "autoUpdater-requestHeaders-set" , value ) ;
2020-07-08 11:16:00 +02:00
}
}
2020-08-21 12:13:38 +02:00
/// <summary>
/// Emitted when there is an error while updating.
/// </summary>
public event Action < string > OnError
2019-05-23 02:59:30 +02:00
{
add
{
if ( _error = = null )
{
2021-07-12 19:50:39 +02:00
BridgeConnector . On < string > ( "autoUpdater-error" + GetHashCode ( ) , ( message ) = >
2019-05-23 02:59:30 +02:00
{
_error ( message . ToString ( ) ) ;
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "register-autoUpdater-error-event" , GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
_error + = value ;
}
remove
{
_error - = value ;
if ( _error = = null )
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdater-error" + GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
}
private event Action < string > _error ;
/// <summary>
/// Emitted when checking if an update has started.
/// </summary>
public event Action OnCheckingForUpdate
{
add
{
if ( _checkingForUpdate = = null )
{
2021-07-12 19:50:39 +02:00
BridgeConnector . On ( "autoUpdater-checking-for-update" + GetHashCode ( ) , ( ) = >
2019-05-23 02:59:30 +02:00
{
_checkingForUpdate ( ) ;
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "register-autoUpdater-checking-for-update-event" , GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
_checkingForUpdate + = value ;
}
remove
{
_checkingForUpdate - = value ;
if ( _checkingForUpdate = = null )
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdater-checking-for-update" + GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
}
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
{
if ( _updateAvailable = = null )
{
2021-07-12 19:50:39 +02:00
BridgeConnector . On < UpdateInfo > ( "autoUpdater-update-available" + GetHashCode ( ) , ( updateInfo ) = >
2019-05-23 02:59:30 +02:00
{
2021-07-12 19:50:39 +02:00
_updateAvailable ( updateInfo ) ;
2019-05-23 02:59:30 +02:00
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "register-autoUpdater-update-available-event" , GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
_updateAvailable + = value ;
}
remove
{
_updateAvailable - = value ;
if ( _updateAvailable = = null )
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdater-update-available" + GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
}
private event Action < UpdateInfo > _updateAvailable ;
/// <summary>
/// Emitted when there is no available update.
/// </summary>
public event Action < UpdateInfo > OnUpdateNotAvailable
{
add
{
if ( _updateNotAvailable = = null )
{
2021-07-12 19:50:39 +02:00
BridgeConnector . On < UpdateInfo > ( "autoUpdater-update-not-available" + GetHashCode ( ) , ( updateInfo ) = >
2019-05-23 02:59:30 +02:00
{
2021-07-12 19:50:39 +02:00
_updateNotAvailable ( updateInfo ) ;
2019-05-23 02:59:30 +02:00
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "register-autoUpdater-update-not-available-event" , GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
_updateNotAvailable + = value ;
}
remove
{
_updateNotAvailable - = value ;
if ( _updateNotAvailable = = null )
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdater-update-not-available" + GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
}
private event Action < UpdateInfo > _updateNotAvailable ;
/// <summary>
/// Emitted on download progress.
/// </summary>
public event Action < ProgressInfo > OnDownloadProgress
{
add
{
if ( _downloadProgress = = null )
{
2021-07-12 19:50:39 +02:00
BridgeConnector . On < ProgressInfo > ( "autoUpdater-download-progress" + GetHashCode ( ) , ( progressInfo ) = >
2019-05-23 02:59:30 +02:00
{
2021-07-12 19:50:39 +02:00
_downloadProgress ( progressInfo ) ;
2019-05-23 02:59:30 +02:00
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "register-autoUpdater-download-progress-event" , GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
_downloadProgress + = value ;
}
remove
{
_downloadProgress - = value ;
if ( _downloadProgress = = null )
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdater-download-progress" + GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
}
private event Action < ProgressInfo > _downloadProgress ;
/// <summary>
/// Emitted on download complete.
/// </summary>
public event Action < UpdateInfo > OnUpdateDownloaded
{
add
{
if ( _updateDownloaded = = null )
{
2021-07-12 19:50:39 +02:00
BridgeConnector . On < UpdateInfo > ( "autoUpdater-update-downloaded" + GetHashCode ( ) , ( updateInfo ) = >
2019-05-23 02:59:30 +02:00
{
2021-07-12 19:50:39 +02:00
_updateDownloaded ( updateInfo ) ;
2019-05-23 02:59:30 +02:00
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "register-autoUpdater-update-downloaded-event" , GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
_updateDownloaded + = value ;
}
remove
{
_updateDownloaded - = value ;
if ( _updateDownloaded = = null )
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdater-update-downloaded" + GetHashCode ( ) ) ;
2019-05-23 02:59:30 +02:00
}
}
private event Action < UpdateInfo > _updateDownloaded ;
2019-05-20 01:08:26 +02:00
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 ( )
{
2021-08-17 16:28:07 +02:00
var taskCompletionSource = new TaskCompletionSource < UpdateCheckResult > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
2019-05-20 01:08:26 +02:00
string guid = Guid . NewGuid ( ) . ToString ( ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . On < UpdateCheckResult > ( "autoUpdaterCheckForUpdatesComplete" + guid , ( updateCheckResult ) = >
2020-07-08 11:16:00 +02:00
{
try
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdaterCheckForUpdatesComplete" + guid ) ;
BridgeConnector . Off ( "autoUpdaterCheckForUpdatesError" + guid ) ;
taskCompletionSource . SetResult ( updateCheckResult ) ;
2020-07-08 11:16:00 +02:00
}
catch ( Exception ex )
{
taskCompletionSource . SetException ( ex ) ;
}
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . On < string > ( "autoUpdaterCheckForUpdatesError" + guid , ( error ) = >
2019-05-20 01:08:26 +02:00
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdaterCheckForUpdatesComplete" + guid ) ;
BridgeConnector . Off ( "autoUpdaterCheckForUpdatesError" + guid ) ;
2020-07-08 11:16:00 +02:00
string message = "An error occurred in CheckForUpdatesAsync" ;
if ( error ! = null & & ! string . IsNullOrEmpty ( error . ToString ( ) ) )
message = JsonConvert . SerializeObject ( error ) ;
2020-08-21 12:13:38 +02:00
taskCompletionSource . SetException ( new Exception ( message ) ) ;
2019-05-20 01:08:26 +02:00
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdaterCheckForUpdates" , guid ) ;
2019-05-20 01:08:26 +02:00
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 ( )
{
2021-08-17 16:28:07 +02:00
var taskCompletionSource = new TaskCompletionSource < UpdateCheckResult > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
2019-05-20 01:08:26 +02:00
string guid = Guid . NewGuid ( ) . ToString ( ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . On < UpdateCheckResult > ( "autoUpdaterCheckForUpdatesAndNotifyComplete" + guid , ( updateCheckResult ) = >
2020-07-08 11:16:00 +02:00
{
try
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdaterCheckForUpdatesAndNotifyComplete" + guid ) ;
BridgeConnector . Off ( "autoUpdaterCheckForUpdatesAndNotifyError" + guid ) ;
2020-07-08 11:16:00 +02:00
if ( updateCheckResult = = null )
taskCompletionSource . SetResult ( null ) ;
else
2021-07-12 19:50:39 +02:00
taskCompletionSource . SetResult ( updateCheckResult ) ;
2020-07-08 11:16:00 +02:00
}
catch ( Exception ex )
{
taskCompletionSource . SetException ( ex ) ;
}
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . On < string > ( "autoUpdaterCheckForUpdatesAndNotifyError" + guid , ( error ) = >
2019-05-20 01:08:26 +02:00
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdaterCheckForUpdatesAndNotifyComplete" + guid ) ;
BridgeConnector . Off ( "autoUpdaterCheckForUpdatesAndNotifyError" + guid ) ;
2020-07-08 11:16:00 +02:00
string message = "An error occurred in autoUpdaterCheckForUpdatesAndNotify" ;
if ( error ! = null )
message = JsonConvert . SerializeObject ( error ) ;
2020-08-21 12:13:38 +02:00
taskCompletionSource . SetException ( new Exception ( message ) ) ;
2019-05-20 01:08:26 +02:00
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdaterCheckForUpdatesAndNotify" , guid ) ;
2019-05-20 01:08:26 +02:00
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 )
{
2021-08-20 15:06:58 +02:00
BridgeConnector . EmitSync ( "autoUpdaterQuitAndInstall" , isSilent , isForceRunAfter ) ;
2019-05-20 01:08:26 +02:00
}
2019-05-23 23:10:45 +02:00
/// <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 ( )
{
2021-08-17 16:28:07 +02:00
var taskCompletionSource = new TaskCompletionSource < string > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
2019-05-23 23:10:45 +02:00
string guid = Guid . NewGuid ( ) . ToString ( ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . On < string > ( "autoUpdaterDownloadUpdateComplete" + guid , ( downloadedPath ) = >
2019-05-23 23:10:45 +02:00
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdaterDownloadUpdateComplete" + guid ) ;
2019-05-23 23:10:45 +02:00
taskCompletionSource . SetResult ( downloadedPath . ToString ( ) ) ;
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdaterDownloadUpdate" , guid ) ;
2019-05-23 23:10:45 +02:00
return taskCompletionSource . Task ;
}
/// <summary>
/// Feed URL.
/// </summary>
/// <returns>Feed URL.</returns>
public Task < string > GetFeedURLAsync ( )
{
2021-08-17 16:28:07 +02:00
var taskCompletionSource = new TaskCompletionSource < string > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
2019-05-23 23:10:45 +02:00
string guid = Guid . NewGuid ( ) . ToString ( ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . On < string > ( "autoUpdaterGetFeedURLComplete" + guid , ( downloadedPath ) = >
2019-05-23 23:10:45 +02:00
{
2021-07-12 19:50:39 +02:00
BridgeConnector . Off ( "autoUpdaterGetFeedURLComplete" + guid ) ;
2019-05-23 23:10:45 +02:00
taskCompletionSource . SetResult ( downloadedPath . ToString ( ) ) ;
} ) ;
2021-07-12 19:50:39 +02:00
BridgeConnector . Emit ( "autoUpdaterGetFeedURL" , guid ) ;
2019-05-23 23:10:45 +02:00
return taskCompletionSource . Task ;
}
2019-05-20 01:08:26 +02:00
}
}