ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Downloader.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <QDir>
9 #include <QFile>
10 #include <QProcess>
11 #include <QDateTime>
12 #include <QMessageBox>
13 #include <QNetworkReply>
14 #include <QDesktopServices>
15 #include <QNetworkAccessManager>
16 
17 #include <math.h>
18 
20 
21 static const QString PARTIAL_DOWN(".part");
22 
23 Downloader::Downloader(QWidget *parent)
24  : QWidget(parent)
25 {
26  m_ui = new Ui::Downloader;
27  m_ui->setupUi(this);
28 
29  /* Initialize private members */
30  m_manager = new QNetworkAccessManager();
31 
32  /* Initialize internal values */
33  m_url = "";
34  m_fileName = "";
35  m_startTime = 0;
36  m_useCustomProcedures = false;
37  m_mandatoryUpdate = false;
38 
39  /* Set download directory */
40  m_downloadDir.setPath(QDir::homePath() + "/Downloads/");
41 
42  /* Make the window look like a modal dialog */
43  setWindowIcon(QIcon());
44  setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
45 
46  /* Configure the appearance and behavior of the buttons */
47  m_ui->openButton->setEnabled(false);
48  m_ui->openButton->setVisible(false);
49  connect(m_ui->stopButton, SIGNAL(clicked()), this, SLOT(cancelDownload()));
50  connect(m_ui->openButton, SIGNAL(clicked()), this, SLOT(installUpdate()));
51 
52  /* Resize to fit */
53  setFixedSize(minimumSizeHint());
54 }
55 
57 {
58  delete m_ui;
59  delete m_reply;
60  delete m_manager;
61 }
62 
69 {
70  return m_useCustomProcedures;
71 }
72 
80 void Downloader::setUrlId(const QString &url)
81 {
82  m_url = url;
83 }
84 
88 void Downloader::startDownload(const QUrl &url)
89 {
90  /* reset UI */
91  m_ui->progressBar->setValue(0);
92  m_ui->stopButton->setText(tr("Stop"));
93  m_ui->downloadLabel->setText(tr("Downloading updates"));
94  m_ui->timeLabel->setText(tr("Time remaining") + ": " + tr("unknown"));
95 
96  /* Configure the network request */
97  QNetworkRequest request(url);
98  if (!m_userAgentString.isEmpty())
99  request.setRawHeader("User-Agent", m_userAgentString.toUtf8());
100 
101  /* Start download */
102  m_reply = m_manager->get(request);
103  m_startTime = QDateTime::currentDateTime().toSecsSinceEpoch();
104 
105  /* Ensure that downloads directory exists */
106  if (!m_downloadDir.exists())
107  m_downloadDir.mkpath(".");
108 
109  /* Remove old downloads */
110  QFile::remove(m_downloadDir.filePath(m_fileName));
111  QFile::remove(m_downloadDir.filePath(m_fileName + PARTIAL_DOWN));
112 
113  /* Update UI when download progress changes or download finishes */
114  connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(updateProgress(qint64, qint64)));
115  connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
116  connect(m_reply, SIGNAL(redirected(QUrl)), this, SLOT(startDownload(QUrl)));
117 
118  showNormal();
119 }
120 
124 void Downloader::setFileName(const QString &file)
125 {
126  m_fileName = file;
127 
128  if (m_fileName.isEmpty())
129  m_fileName = "QSU_Update.bin";
130 }
131 
135 void Downloader::setUserAgentString(const QString &agent)
136 {
137  m_userAgentString = agent;
138 }
139 
140 void Downloader::finished()
141 {
142  /* Rename file */
143  QFile::rename(m_downloadDir.filePath(m_fileName + PARTIAL_DOWN), m_downloadDir.filePath(m_fileName));
144 
145  /* Notify application */
146  emit downloadFinished(m_url, m_downloadDir.filePath(m_fileName));
147 
148  /* Install the update */
149  m_reply->close();
150  installUpdate();
151  setVisible(false);
152 }
153 
159 void Downloader::openDownload()
160 {
161  if (!m_fileName.isEmpty())
162  {
163  QDesktopServices::openUrl(QUrl::fromLocalFile(m_downloadDir.filePath(m_fileName)));
164  }
165  else
166  {
167  QMessageBox::critical(this, tr("Error"), tr("Cannot find downloaded update!"), QMessageBox::Close);
168  }
169 }
170 
179 void Downloader::installUpdate()
180 {
182  return;
183 
184  /* Update labels */
185  m_ui->stopButton->setText(tr("Close"));
186  m_ui->downloadLabel->setText(tr("Download complete!"));
187  m_ui->timeLabel->setText(tr("The installer will open separately") + "...");
188 
189  /* Ask the user to install the download */
190  QMessageBox box;
191  box.setIcon(QMessageBox::Question);
192  box.setDefaultButton(QMessageBox::Ok);
193  box.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
194  box.setInformativeText(tr("Click \"OK\" to begin installing the update"));
195 
196  QString text = tr("In order to install the update, you may need to "
197  "quit the application.");
198 
199  if (m_mandatoryUpdate)
200  text = tr("In order to install the update, you may need to "
201  "quit the application. This is a mandatory update, exiting now will close the application");
202 
203  box.setText("<h3>" + text + "</h3>");
204 
205  /* User wants to install the download */
206  if (box.exec() == QMessageBox::Ok)
207  {
209  openDownload();
210  }
211  /* Wait */
212  else
213  {
214  if (m_mandatoryUpdate)
215  {
216  QString installerFile = m_downloadDir.filePath(m_fileName);
217  QMessageBox::information(this, "", tr("Please install from <i>%1</i> mannually!").arg(installerFile));
218  QApplication::quit();
219  }
220 
221  m_ui->openButton->setEnabled(true);
222  m_ui->openButton->setVisible(true);
223  m_ui->timeLabel->setText(tr("Click the \"Open\" button to "
224  "apply the update"));
225  }
226 }
227 
232 void Downloader::cancelDownload()
233 {
234  if (!m_reply->isFinished())
235  {
236  QMessageBox box;
237  box.setWindowTitle(tr("Updater"));
238  box.setIcon(QMessageBox::Question);
239  box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
240 
241  QString text = tr("Are you sure you want to cancel the download?");
242  if (m_mandatoryUpdate)
243  {
244  text = tr("Are you sure you want to cancel the download? This is a mandatory update, exiting now will close "
245  "the application");
246  }
247  box.setText(text);
248 
249  if (box.exec() == QMessageBox::Yes)
250  {
251  hide();
252  m_reply->abort();
253  if (m_mandatoryUpdate)
254  QApplication::quit();
255  }
256  }
257  else
258  {
259  if (m_mandatoryUpdate)
260  QApplication::quit();
261 
262  hide();
263  }
264 }
265 
269 void Downloader::saveFile(qint64 received, qint64 total)
270 {
271  Q_UNUSED(received);
272  Q_UNUSED(total);
273 
274  /* Check if we need to redirect */
275  QUrl url = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
276  if (!url.isEmpty())
277  {
278  startDownload(url);
279  return;
280  }
281 
282  /* Save downloaded data to disk */
283  QFile file(m_downloadDir.filePath(m_fileName + PARTIAL_DOWN));
284  if (file.open(QIODevice::WriteOnly | QIODevice::Append))
285  {
286  file.write(m_reply->readAll());
287  file.close();
288  }
289 }
290 
296 void Downloader::calculateSizes(qint64 received, qint64 total)
297 {
298  QString totalSize;
299  QString receivedSize;
300 
301  if (total < 1024)
302  totalSize = tr("%1 bytes").arg(total);
303 
304  else if (total < 1048576)
305  totalSize = tr("%1 KB").arg(round(total / 1024));
306 
307  else
308  totalSize = tr("%1 MB").arg(round(total / 1048576));
309 
310  if (received < 1024)
311  receivedSize = tr("%1 bytes").arg(received);
312 
313  else if (received < 1048576)
314  receivedSize = tr("%1 KB").arg(received / 1024);
315 
316  else
317  receivedSize = tr("%1 MB").arg(received / 1048576);
318 
319  m_ui->downloadLabel->setText(tr("Downloading updates") + " (" + receivedSize + " " + tr("of") + " " + totalSize
320  + ")");
321 }
322 
327 void Downloader::updateProgress(qint64 received, qint64 total)
328 {
329  if (total > 0)
330  {
331  m_ui->progressBar->setMinimum(0);
332  m_ui->progressBar->setMaximum(100);
333  m_ui->progressBar->setValue((received * 100) / total);
334 
335  calculateSizes(received, total);
336  calculateTimeRemaining(received, total);
337  saveFile(received, total);
338  }
339 
340  else
341  {
342  m_ui->progressBar->setMinimum(0);
343  m_ui->progressBar->setMaximum(0);
344  m_ui->progressBar->setValue(-1);
345  m_ui->downloadLabel->setText(tr("Downloading Updates") + "...");
346  m_ui->timeLabel->setText(QString("%1: %2").arg(tr("Time Remaining")).arg(tr("Unknown")));
347  }
348 }
349 
358 void Downloader::calculateTimeRemaining(qint64 received, qint64 total)
359 {
360  uint difference = QDateTime::currentDateTime().toSecsSinceEpoch() - m_startTime;
361 
362  if (difference > 0)
363  {
364  QString timeString;
365  qreal timeRemaining = (total - received) / (received / difference);
366 
367  if (timeRemaining > 7200)
368  {
369  timeRemaining /= 3600;
370  int hours = int(timeRemaining + 0.5);
371 
372  if (hours > 1)
373  timeString = tr("about %1 hours").arg(hours);
374  else
375  timeString = tr("about one hour");
376  }
377 
378  else if (timeRemaining > 60)
379  {
380  timeRemaining /= 60;
381  int minutes = int(timeRemaining + 0.5);
382 
383  if (minutes > 1)
384  timeString = tr("%1 minutes").arg(minutes);
385  else
386  timeString = tr("1 minute");
387  }
388 
389  else if (timeRemaining <= 60)
390  {
391  int seconds = int(timeRemaining + 0.5);
392 
393  if (seconds > 1)
394  timeString = tr("%1 seconds").arg(seconds);
395  else
396  timeString = tr("1 second");
397  }
398 
399  m_ui->timeLabel->setText(tr("Time remaining") + ": " + timeString);
400  }
401 }
402 
406 qreal Downloader::round(const qreal &input)
407 {
408  return static_cast<qreal>(roundf(static_cast<float>(input) * 100) / 100);
409 }
410 
411 QString Downloader::downloadDir() const
412 {
413  return m_downloadDir.absolutePath();
414 }
415 
416 void Downloader::setDownloadDir(const QString &downloadDir)
417 {
418  if (m_downloadDir.absolutePath() != downloadDir)
419  m_downloadDir.setPath(downloadDir);
420 }
421 
426 void Downloader::setMandatoryUpdate(const bool mandatory_update)
427 {
428  m_mandatoryUpdate = mandatory_update;
429 }
430 
439 {
440  m_useCustomProcedures = custom;
441 }
static const QString PARTIAL_DOWN(".part")
Downloader(QWidget *parent=nullptr)
Definition: Downloader.cpp:23
void setUserAgentString(const QString &agent)
Definition: Downloader.cpp:135
bool useCustomInstallProcedures() const
Definition: Downloader.cpp:68
void downloadFinished(const QString &url, const QString &filepath)
QString downloadDir() const
Definition: Downloader.cpp:411
void startDownload(const QUrl &url)
Definition: Downloader.cpp:88
void setDownloadDir(const QString &downloadDir)
Definition: Downloader.cpp:416
void setFileName(const QString &file)
Definition: Downloader.cpp:124
void setMandatoryUpdate(const bool mandatory_update)
Definition: Downloader.cpp:426
void setUseCustomInstallProcedures(const bool custom)
Definition: Downloader.cpp:438
void setUrlId(const QString &url)
Definition: Downloader.cpp:80
unsigned int uint
Definition: cutil_math.h:28
Tensor Append(const Tensor &self, const Tensor &other, const utility::optional< int64_t > &axis)
Appends the two tensors, along the given axis into a new tensor. Both the tensors must have same data...
#define seconds
Definition: rename.h:375