Audacity  3.0.3
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 
32 static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime";
33 
34 static BoolSetting
35  prefUpdatesNoticeShown(wxT("/Update/UpdateNoticeShown"), false);
36 
37 using Clock = std::chrono::system_clock;
38 using TimePoint = Clock::time_point;
39 using Duration = TimePoint::duration;
40 
41 #if UPDATE_LOCAL_TESTING == 1
42 constexpr Duration updatesCheckInterval = std::chrono::minutes(2);
43 #else
44 constexpr Duration updatesCheckInterval = std::chrono::hours(12);
45 #endif
46 
47 enum { ID_TIMER = wxID_HIGHEST + 1 };
48 
49 BEGIN_EVENT_TABLE(UpdateManager, wxEvtHandler)
52 
54 {
55  static UpdateManager updateManager;
56 
57  return updateManager;
58 }
59 
61 {
62  auto& instance = GetInstance();
63 
64  // Show the dialog only once.
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 
92 void 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 
264 void 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 }
NoUpdatesAvailableDialog.h
BasicUI::MakeProgress
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:263
audacity::network_manager::IResponse
Interface, that provides access to the data from the HTTP response.
Definition: IResponse.h:50
UpdateManager::GetInstance
static UpdateManager & GetInstance()
Definition: UpdateManager.cpp:53
UpdateManager::GetVersionPatch
VersionPatch GetVersionPatch() const
Definition: UpdateManager.cpp:87
VersionPatch::version
VersionId version
Definition: VersionPatch.h:20
flag
static std::once_flag flag
Definition: WaveformView.cpp:1119
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
UpdateManager::mTimer
wxTimer mTimer
Definition: UpdateManager.h:49
BasicUI::ShowErrorDialog
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:233
ID_TIMER
@ ID_TIMER
Definition: UpdateManager.cpp:47
Setting::Write
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:172
UpdateManager::Start
static void Start()
Definition: UpdateManager.cpp:60
Format
Abstract base class used in importing a file.
UpdateManager.h
Declare a class that handles managing of updates.
BoolSetting
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:204
NoUpdatesAvailableDialog
Information dialog about no updates available, that allows to navigate to settings quickly.
Definition: NoUpdatesAvailableDialog.h:19
XO
#define XO(s)
Definition: Internat.h:31
XC
#define XC(s, c)
Definition: Internat.h:37
audacity::network_manager::Request
Definition: Request.h:28
Request.h
Declare a class for constructing HTTP requests.
Duration
TimePoint::duration Duration
Definition: UpdateManager.cpp:39
UpdateManager::mVersionPatch
VersionPatch mVersionPatch
Definition: UpdateManager.h:47
IResponse.h
Declare an interface for HTTP response.
Setting::Read
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:128
UpdateDataParser::Parse
bool Parse(const VersionPatch::UpdateDataFormat &updateData, VersionPatch *versionPatch)
Parsing from update data format to VersionPatch fields.
Definition: UpdateDataParser.cpp:22
DefaultUpdatesCheckingFlag
BoolSetting DefaultUpdatesCheckingFlag
Definition: Prefs.cpp:65
UpdateManager::mAudacityInstaller
std::ofstream mAudacityInstaller
Definition: UpdateManager.h:59
NetworkManager.h
Declare a class for preforming HTTP requests.
BasicUI::Get
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:26
updatesCheckInterval
constexpr Duration updatesCheckInterval
Definition: UpdateManager.cpp:44
UpdateManager::mUpdateDataParser
UpdateDataParser mUpdateDataParser
Definition: UpdateManager.h:46
audacity::network_manager::NetworkManager::GetInstance
static NetworkManager & GetInstance()
Definition: NetworkManager.cpp:36
CurrentBuildVersion
static VersionId CurrentBuildVersion()
Return version (VersionId) object with current Audacity build version.
Definition: VersionId.h:46
UpdateManager::mUpdateMutex
std::mutex mUpdateMutex
Definition: UpdateManager.h:61
UpdatePopupDialog
Show dialog window with update information for the user.
Definition: UpdatePopupDialog.h:22
prefsUpdateScheduledTime
static const char * prefsUpdateScheduledTime
Definition: UpdateManager.cpp:32
BasicUI::ErrorDialogOptions
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:49
VersionPatch::download
wxString download
Definition: VersionPatch.h:22
BasicUI.h
Toolkit-neutral facade for basic user interface services.
TimePoint
Clock::time_point TimePoint
Definition: UpdateManager.cpp:38
UpdatePopupDialog.h
Define a dialog for notifying users about new version available.
audacity::network_manager::NetworkError::NoError
@ NoError
audacity::network_manager::NetworkManager::doGet
ResponsePtr doGet(const Request &request)
Definition: NetworkManager.cpp:48
VersionPatch::UpdateDataFormat
std::string UpdateDataFormat
Definition: VersionPatch.h:16
BasicUI
Definition: Export.h:39
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
VersionPatch
A structure that describes patch fields.
Definition: VersionPatch.h:15
AudioIO.h
UpdateManager
A class that managing of updates.
Definition: UpdateManager.h:34
UpdateManager::GetUpdates
void GetUpdates(bool ignoreNetworkErrors, bool configurableNotification)
Definition: UpdateManager.cpp:92
UpdateManager::IsTimeForUpdatesChecking
bool IsTimeForUpdatesChecking()
Scheduling update time for avoiding multiplying update notifications.
Definition: UpdateManager.cpp:280
UpdateNoticeDialog.h
Define a dialog to notify the user about automatic update checking.
UpdateManager::OnTimer
void OnTimer(wxTimerEvent &event)
Definition: UpdateManager.cpp:264
UpdateManager::mOnProgress
bool mOnProgress
Definition: UpdateManager.h:62
AudioIO::Get
static AudioIO * Get()
Definition: AudioIO.cpp:141
UpdateManager::mAudacityInstallerPath
std::string mAudacityInstallerPath
Definition: UpdateManager.h:58
Clock
std::chrono::system_clock Clock
Definition: UpdateManager.cpp:37
END_EVENT_TABLE
END_EVENT_TABLE()
prefUpdatesNoticeShown
static BoolSetting prefUpdatesNoticeShown(wxT("/Update/UpdateNoticeShown"), false)
UpdateManager::mProgressDialog
std::unique_ptr< BasicUI::ProgressDialog > mProgressDialog
Definition: UpdateManager.h:56
UpdateNoticeDialog
Dialog, that notifies the users about automatic updates checking.
Definition: UpdateNoticeDialog.h:21