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#if AUDACITY_BUILD_LEVEL == 0
95 const auto url = "https://updates.audacityteam.org/builds/alpha.xml";
96#elif AUDACITY_BUILD_LEVEL == 1
97 const auto url = "https://updates.audacityteam.org/builds/beta.xml";
98#else
99 const auto url = "https://updates.audacityteam.org/feed/latest.xml";
100#endif
101
102 const audacity::network_manager::Request request(url);
104
105 response->setRequestFinishedCallback([response, ignoreNetworkErrors, configurableNotification, this](audacity::network_manager::IResponse*) {
106
107 // We don't' want to duplicate the updates checking if that already launched.
108 {
109 std::lock_guard<std::mutex> lock(mUpdateMutex);
110 if (mOnProgress)
111 {
112 response->abort();
113 return;
114 }
115 mOnProgress = true;
116 }
117
118 using namespace BasicUI;
119 auto gAudioIO = AudioIO::Get();
120 if (response->getError() != audacity::network_manager::NetworkError::NoError)
121 {
122 if (!ignoreNetworkErrors)
123 {
124 gAudioIO->CallAfterRecording([] {ShowErrorDialog( {},
125 XC("Error checking for update", "update dialog"),
126 XC("Unable to connect to Audacity update server.", "update dialog"),
127 wxString(),
128 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport });
129 });
130 }
131
132 mOnProgress = false;
133 return;
134 }
135
137 {
138 if (!ignoreNetworkErrors)
139 {
140 gAudioIO->CallAfterRecording([] {ShowErrorDialog( {},
141 XC("Error checking for update", "update dialog"),
142 XC("Update data was corrupted.", "update dialog"),
143 wxString(),
144 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport });
145 });
146 }
147
148 mOnProgress = false;
149 return;
150 }
151
152#if UPDATE_LOCAL_TESTING == 0
154#endif
155 {
156 gAudioIO->CallAfterRecording([this, ignoreNetworkErrors, configurableNotification] {
157 UpdatePopupDialog dlg(nullptr, mVersionPatch, configurableNotification);
158 const int code = dlg.ShowModal();
159
160 if (code == wxID_YES)
161 {
162 const audacity::network_manager::Request downloadRequest(mVersionPatch.download.ToStdString());
163 auto downloadResponse = audacity::network_manager::NetworkManager::GetInstance().doGet(downloadRequest);
164
165 // Called once, when downloading is real will finish.
166 downloadResponse->setRequestFinishedCallback([downloadResponse, ignoreNetworkErrors, this](audacity::network_manager::IResponse*) {
167 // First - close all opened resources.
168 wxTheApp->CallAfter([this]{ mProgressDialog.reset(); });
169
170 if (mAudacityInstaller.is_open())
171 mAudacityInstaller.close();
172
173 if (downloadResponse->getError() != audacity::network_manager::NetworkError::NoError)
174 {
175 if (!ignoreNetworkErrors)
176 {
177 wxTheApp->CallAfter([] {ShowErrorDialog( {},
178 XC("Error downloading update", "update dialog"),
179 XC("Can't open the Audacity download link.", "update dialog"),
180 wxString(),
181 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport });
182 });
183 }
184
185 mOnProgress = false;
186 return;
187 }
188
189 const wxPlatformInfo& info = wxPlatformInfo::Get();
190 if ((info.GetOperatingSystemId() & wxOS_WINDOWS) ||
191 info.GetOperatingSystemId() & wxOS_MAC)
192 {
193 if (wxFileName(mAudacityInstallerPath).Exists())
194 {
195 std::string cmd = info.GetOperatingSystemId() & wxOS_MAC ? "Open " + mAudacityInstallerPath : mAudacityInstallerPath;
196 wxTheApp->CallAfter([cmd] { wxExecute(cmd, wxEXEC_ASYNC); });
197 }
198 }
199
200 mOnProgress = false;
201 }
202 );
203
204 auto audacityPatchFilename = wxFileName(mVersionPatch.download).GetName();
205
206 mProgressDialog = BasicUI::MakeProgress(XO("Audacity update"), XO("Downloading %s").Format(audacityPatchFilename));
207 wxASSERT(mProgressDialog);
208
209 wxString installerExtension;
210 const wxPlatformInfo& info = wxPlatformInfo::Get();
211 if (info.GetOperatingSystemId() & wxOS_WINDOWS)
212 installerExtension = "exe";
213 else if(info.GetOperatingSystemId() & wxOS_MAC)
214 installerExtension = "dmg";
215
216 mAudacityInstallerPath = wxFileName(
217 wxStandardPaths::Get().GetUserDir(wxStandardPaths::Dir_Downloads), audacityPatchFilename, installerExtension)
218 .GetFullPath()
219 .ToStdString();
220
221 mAudacityInstaller.open(mAudacityInstallerPath, std::ios::binary);
222
223 // Called each time, since downloading for update progress status.
224 downloadResponse->setDownloadProgressCallback(
225 [this](int64_t current, int64_t expected) {
226
227 wxTheApp->CallAfter([this, current, expected]{
228 if(mProgressDialog != nullptr)
229 mProgressDialog->Poll(current, expected);
230 });
231 }
232 );
233
234 // Called each time, since downloading for get data.
235 downloadResponse->setOnDataReceivedCallback([downloadResponse, this](audacity::network_manager::IResponse*) {
236 if (downloadResponse->getError() == audacity::network_manager::NetworkError::NoError)
237 {
238 std::vector<char> buffer(downloadResponse->getBytesAvailable());
239
240 size_t bytes = downloadResponse->readData(buffer.data(), buffer.size());
241
242 if (mAudacityInstaller.is_open())
243 mAudacityInstaller.write(buffer.data(), buffer.size());
244 }
245 }
246 );
247
248 }
249 else if (code == wxID_NO)
250 {
251 mOnProgress = false;
252 }
253 });
254 }
255#if UPDATE_LOCAL_TESTING == 0
256 else // mVersionPatch.version > CurrentBuildVersion()
257 {
258 // That also shows, that updates checking was called manually from menu.
259 if (!configurableNotification)
260 {
261 gAudioIO->CallAfterRecording([] {
262 NoUpdatesAvailableDialog(nullptr).ShowModal();
263 });
264 }
265
266 mOnProgress = false;
267 }
268#endif
269 });
270}
271
272void UpdateManager::OnTimer(wxTimerEvent& WXUNUSED(event))
273{
274 bool updatesCheckingEnabled = DefaultUpdatesCheckingFlag->Read();
275
276#if UPDATE_LOCAL_TESTING == 0
277 if (updatesCheckingEnabled && IsTimeForUpdatesChecking())
278#endif
279 {
280 GetUpdates(true, true);
281 }
282
283 mTimer.StartOnce(std::chrono::duration_cast<std::chrono::milliseconds>(
285 .count());
286}
287
289{
290 // We use atoll here, so there is no need to handle the exception,
291 // if prefsUpdateScheduledTime is corrupted.
292 // atoll will return 0 on failure, which suits us well.
293 const TimePoint nextUpdatesCheckingTime(std::chrono::milliseconds(
294 atoll(gPrefs->Read(prefsUpdateScheduledTime, "0").c_str())));
295
296 // Get current time
297 const TimePoint currentTime = Clock::now();
298
299 // If next update time 0 or less then current time -> show update dialog,
300 // else this condition allow us to avoid from duplicating update notifications.
301 if (nextUpdatesCheckingTime < currentTime)
302 {
303
304 // Round down the nextUpdatesChecking time to a day.
305 // This is required to ensure, that update is
306 // checked daily
307 using DayDuration =
308 std::chrono::duration<int32_t, std::ratio<60 * 60 * 24>>;
309
310 const auto postponeUpdateUntil =
311 std::chrono::time_point_cast<DayDuration>(
312 currentTime) + DayDuration(1);
313
314 const std::chrono::milliseconds postponeUpdateUntilMS(
315 postponeUpdateUntil.time_since_epoch());
316
317 gPrefs->Write(
319 wxString(std::to_string(postponeUpdateUntilMS.count())));
320
321 gPrefs->Flush();
322
323 return true;
324 }
325
326 return false;
327}
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
END_EVENT_TABLE()
XO("Cut/Copy/Paste")
Declare an interface for HTTP response.
#define XC(s, c)
Definition: Internat.h:37
Declare a class for performing HTTP requests.
StickySetting< BoolSetting > DefaultUpdatesCheckingFlag
Definition: Prefs.cpp:63
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
Declare a class for constructing HTTP requests.
std::chrono::system_clock Clock
static const char * prefsUpdateScheduledTime
Clock::time_point TimePoint
@ ID_TIMER
TimePoint::duration Duration
constexpr Duration updatesCheckInterval
static BoolSetting prefUpdatesNoticeShown(wxT("/Update/UpdateNoticeShown"), false)
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:47
static std::once_flag flag
static AudioIO * Get()
Definition: AudioIO.cpp:126
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:346
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:259
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
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.
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
virtual bool Read(const wxString &key, bool *value) const =0
Interface, that provides access to the data from the HTTP response.
Definition: IResponse.h:113
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:272
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:202
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:302
FrameStatistics & GetInstance() noexcept
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:52
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