Audacity 3.2.0
UpdateManager.cpp
Go to the documentation of this file.
1/*!********************************************************************
2 Audacity: A Digital Audio Editor
3
4 @file UpdateManager.cpp
5 @brief Declare a class that handles managing of updates.
6
7 Anton Gerasimov
8 **********************************************************************/
9
10#include "UpdateManager.h"
11
12#include "UpdatePopupDialog.h"
13#include "UpdateNoticeDialog.h"
15
16#include "AudioIO.h"
17#include "BasicUI.h"
18#include "NetworkManager.h"
19#include "IResponse.h"
20#include "Request.h"
21
22#include <wx/utils.h>
23#include <wx/frame.h>
24#include <wx/app.h>
25#include <wx/stdpaths.h>
26#include <wx/filename.h>
27
28#include <cstdint>
29
30#define UPDATE_LOCAL_TESTING 0
31
32static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime";
33
34static BoolSetting
35 prefUpdatesNoticeShown(wxT("/Update/UpdateNoticeShown"), false);
36
37using Clock = std::chrono::system_clock;
38using TimePoint = Clock::time_point;
39using Duration = TimePoint::duration;
40
41#if UPDATE_LOCAL_TESTING == 1
42constexpr Duration updatesCheckInterval = std::chrono::minutes(2);
43#else
44constexpr Duration updatesCheckInterval = std::chrono::hours(12);
45#endif
46
47enum { ID_TIMER = wxID_HIGHEST + 1 };
48
49BEGIN_EVENT_TABLE(UpdateManager, wxEvtHandler)
52
54{
55 static UpdateManager updateManager;
56
57 return updateManager;
58}
59
60void UpdateManager::Start(bool suppressModal)
61{
62 auto& instance = GetInstance();
63
64 // Show the dialog only once.
65 if (!suppressModal && !prefUpdatesNoticeShown.Read())
66 {
67 // DefaultUpdatesCheckingFlag survives the "Reset Preferences"
68 // action, so check, if the updates were previously disabled as well.
70 {
71 UpdateNoticeDialog notice(nullptr);
72
73 notice.ShowModal();
74 }
75
77 gPrefs->Flush();
78 }
79
80 static std::once_flag flag;
81 std::call_once(flag, [&instance] {
82 instance.mTimer.SetOwner(&instance, ID_TIMER);
83 instance.mTimer.StartOnce(1);
84 });
85}
86
88{
89 return mVersionPatch;
90}
91
92void UpdateManager::GetUpdates(bool ignoreNetworkErrors, bool configurableNotification)
93{
94 const audacity::network_manager::Request request("https://updates.audacityteam.org/feed/latest.xml");
96
97 response->setRequestFinishedCallback([response, ignoreNetworkErrors, configurableNotification, this](audacity::network_manager::IResponse*) {
98
99 // We don't' want to duplicate the updates checking if that already launched.
100 {
101 std::lock_guard<std::mutex> lock(mUpdateMutex);
102 if (mOnProgress)
103 {
104 response->abort();
105 return;
106 }
107 mOnProgress = true;
108 }
109
110 using namespace BasicUI;
111 auto gAudioIO = AudioIO::Get();
112 if (response->getError() != audacity::network_manager::NetworkError::NoError)
113 {
114 if (!ignoreNetworkErrors)
115 {
116 gAudioIO->CallAfterRecording([] {ShowErrorDialog( {},
117 XC("Error checking for update", "update dialog"),
118 XC("Unable to connect to Audacity update server.", "update dialog"),
119 wxString(),
120 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport });
121 });
122 }
123
124 mOnProgress = false;
125 return;
126 }
127
129 {
130 if (!ignoreNetworkErrors)
131 {
132 gAudioIO->CallAfterRecording([] {ShowErrorDialog( {},
133 XC("Error checking for update", "update dialog"),
134 XC("Update data was corrupted.", "update dialog"),
135 wxString(),
136 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport });
137 });
138 }
139
140 mOnProgress = false;
141 return;
142 }
143
144#if UPDATE_LOCAL_TESTING == 0
146#endif
147 {
148 gAudioIO->CallAfterRecording([this, ignoreNetworkErrors, configurableNotification] {
149 UpdatePopupDialog dlg(nullptr, mVersionPatch, configurableNotification);
150 const int code = dlg.ShowModal();
151
152 if (code == wxID_YES)
153 {
154 const audacity::network_manager::Request downloadRequest(mVersionPatch.download.ToStdString());
155 auto downloadResponse = audacity::network_manager::NetworkManager::GetInstance().doGet(downloadRequest);
156
157 // Called once, when downloading is real will finish.
158 downloadResponse->setRequestFinishedCallback([downloadResponse, ignoreNetworkErrors, this](audacity::network_manager::IResponse*) {
159 // First - close all opened resources.
160 wxTheApp->CallAfter([this]{ mProgressDialog.reset(); });
161
162 if (mAudacityInstaller.is_open())
163 mAudacityInstaller.close();
164
165 if (downloadResponse->getError() != audacity::network_manager::NetworkError::NoError)
166 {
167 if (!ignoreNetworkErrors)
168 {
169 wxTheApp->CallAfter([] {ShowErrorDialog( {},
170 XC("Error downloading update", "update dialog"),
171 XC("Can't open the Audacity download link.", "update dialog"),
172 wxString(),
173 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport });
174 });
175 }
176
177 mOnProgress = false;
178 return;
179 }
180
181 const wxPlatformInfo& info = wxPlatformInfo::Get();
182 if ((info.GetOperatingSystemId() & wxOS_WINDOWS) ||
183 info.GetOperatingSystemId() & wxOS_MAC)
184 {
185 if (wxFileName(mAudacityInstallerPath).Exists())
186 {
187 std::string cmd = info.GetOperatingSystemId() & wxOS_MAC ? "Open " + mAudacityInstallerPath : mAudacityInstallerPath;
188 wxTheApp->CallAfter([cmd] { wxExecute(cmd, wxEXEC_ASYNC); });
189 }
190 }
191
192 mOnProgress = false;
193 }
194 );
195
196 auto audacityPatchFilename = wxFileName(mVersionPatch.download).GetName();
197
198 mProgressDialog = BasicUI::MakeProgress(XO("Audacity update"), XO("Downloading %s").Format(audacityPatchFilename));
199 wxASSERT(mProgressDialog);
200
201 wxString installerExtension;
202 const wxPlatformInfo& info = wxPlatformInfo::Get();
203 if (info.GetOperatingSystemId() & wxOS_WINDOWS)
204 installerExtension = "exe";
205 else if(info.GetOperatingSystemId() & wxOS_MAC)
206 installerExtension = "dmg";
207
208 mAudacityInstallerPath = wxFileName(
209 wxStandardPaths::Get().GetUserDir(wxStandardPaths::Dir_Downloads), audacityPatchFilename, installerExtension)
210 .GetFullPath()
211 .ToStdString();
212
213 mAudacityInstaller.open(mAudacityInstallerPath, std::ios::binary);
214
215 // Called each time, since downloading for update progress status.
216 downloadResponse->setDownloadProgressCallback(
217 [this](int64_t current, int64_t expected) {
218
219 wxTheApp->CallAfter([this, current, expected]{
220 if(mProgressDialog != nullptr)
221 mProgressDialog->Poll(current, expected);
222 });
223 }
224 );
225
226 // Called each time, since downloading for get data.
227 downloadResponse->setOnDataReceivedCallback([downloadResponse, this](audacity::network_manager::IResponse*) {
228 if (downloadResponse->getError() == audacity::network_manager::NetworkError::NoError)
229 {
230 std::vector<char> buffer(downloadResponse->getBytesAvailable());
231
232 size_t bytes = downloadResponse->readData(buffer.data(), buffer.size());
233
234 if (mAudacityInstaller.is_open())
235 mAudacityInstaller.write(buffer.data(), buffer.size());
236 }
237 }
238 );
239
240 }
241 else if (code == wxID_NO)
242 {
243 mOnProgress = false;
244 }
245 });
246 }
247#if UPDATE_LOCAL_TESTING == 0
248 else // mVersionPatch.version > CurrentBuildVersion()
249 {
250 // That also shows, that updates checking was called manually from menu.
251 if (!configurableNotification)
252 {
253 gAudioIO->CallAfterRecording([] {
254 NoUpdatesAvailableDialog(nullptr).ShowModal();
255 });
256 }
257
258 mOnProgress = false;
259 }
260#endif
261 });
262}
263
264void UpdateManager::OnTimer(wxTimerEvent& WXUNUSED(event))
265{
266 bool updatesCheckingEnabled = DefaultUpdatesCheckingFlag.Read();
267
268#if UPDATE_LOCAL_TESTING == 0
269 if (updatesCheckingEnabled && IsTimeForUpdatesChecking())
270#endif
271 {
272 GetUpdates(true, true);
273 }
274
275 mTimer.StartOnce(std::chrono::duration_cast<std::chrono::milliseconds>(
277 .count());
278}
279
281{
282 // We use atoll here, so there is no need to handle the exception,
283 // if prefsUpdateScheduledTime is corrupted.
284 // atoll will return 0 on failure, which suits us well.
285 const TimePoint nextUpdatesCheckingTime(std::chrono::milliseconds(
286 atoll(gPrefs->Read(prefsUpdateScheduledTime, "0").c_str())));
287
288 // Get current time
289 const TimePoint currentTime = Clock::now();
290
291 // If next update time 0 or less then current time -> show update dialog,
292 // else this condition allow us to avoid from duplicating update notifications.
293 if (nextUpdatesCheckingTime < currentTime)
294 {
295
296 // Round down the nextUpdatesChecking time to a day.
297 // This is required to ensure, that update is
298 // checked daily
299 using DayDuration =
300 std::chrono::duration<int32_t, std::ratio<60 * 60 * 24>>;
301
302 const auto postponeUpdateUntil =
303 std::chrono::time_point_cast<DayDuration>(
304 currentTime) + DayDuration(1);
305
306 const std::chrono::milliseconds postponeUpdateUntilMS(
307 postponeUpdateUntil.time_since_epoch());
308
309 gPrefs->Write(
311 wxString(std::to_string(postponeUpdateUntilMS.count())));
312
313 gPrefs->Flush();
314
315 return true;
316 }
317
318 return false;
319}
Toolkit-neutral facade for basic user interface services.
END_EVENT_TABLE()
Declare an interface for HTTP response.
#define XO(s)
Definition: Internat.h:31
#define XC(s, c)
Definition: Internat.h:37
Declare a class for performing HTTP requests.
BoolSetting DefaultUpdatesCheckingFlag
Definition: Prefs.cpp:66
FileConfig * gPrefs
Definition: Prefs.cpp:71
Declare a class for constructing HTTP requests.
std::chrono::system_clock Clock
static const char * prefsUpdateScheduledTime
Clock::time_point TimePoint
TimePoint::duration Duration
constexpr Duration updatesCheckInterval
static BoolSetting prefUpdatesNoticeShown(wxT("/Update/UpdateNoticeShown"), false)
@ ID_TIMER
Declare a class that handles managing of updates.
Define a dialog to notify the user about automatic update checking.
Define a dialog for notifying users about new version available.
static VersionId CurrentBuildVersion()
Return version (VersionId) object with current Audacity build version.
Definition: VersionId.h:46
static std::once_flag flag
static AudioIO * Get()
Definition: AudioIO.cpp:133
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:339
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
Abstract base class used in importing a file.
Information dialog about no updates available, that allows to navigate to settings quickly.
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:252
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
bool Parse(const VersionPatch::UpdateDataFormat &updateData, VersionPatch *versionPatch)
Parsing from update data format to VersionPatch fields.
A class that managing of updates.
Definition: UpdateManager.h:34
void GetUpdates(bool ignoreNetworkErrors, bool configurableNotification)
std::unique_ptr< BasicUI::ProgressDialog > mProgressDialog
Definition: UpdateManager.h:56
static UpdateManager & GetInstance()
std::string mAudacityInstallerPath
Definition: UpdateManager.h:58
std::mutex mUpdateMutex
Definition: UpdateManager.h:61
static void Start(bool suppressModal)
void OnTimer(wxTimerEvent &event)
UpdateDataParser mUpdateDataParser
Definition: UpdateManager.h:46
std::ofstream mAudacityInstaller
Definition: UpdateManager.h:59
VersionPatch GetVersionPatch() const
wxTimer mTimer
Definition: UpdateManager.h:49
VersionPatch mVersionPatch
Definition: UpdateManager.h:47
bool IsTimeForUpdatesChecking()
Scheduling update time for avoiding multiplying update notifications.
Dialog, that notifies the users about automatic updates checking.
Show dialog window with update information for the user.
Interface, that provides access to the data from the HTTP response.
Definition: IResponse.h:50
ResponsePtr doGet(const Request &request)
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
Definition: BasicUI.h:241
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:26
std::unique_ptr< ProgressDialog > MakeProgress(const TranslatableString &title, const TranslatableString &message, unsigned flags=(ProgressShowStop|ProgressShowCancel), const TranslatableString &remainingLabelText={})
Create and display a progress dialog.
Definition: BasicUI.h:271
FrameStatistics & GetInstance() noexcept
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:49
A structure that describes patch fields.
Definition: VersionPatch.h:15
VersionId version
Definition: VersionPatch.h:20
std::string UpdateDataFormat
Definition: VersionPatch.h:16
wxString download
Definition: VersionPatch.h:22