xref: /OK3568_Linux_fs/app/forlinx/flapp/src/plugins/allwinner/browser/downloadmanager.cpp (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the demonstration applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL21$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** $QT_END_LICENSE$
31 **
32 ****************************************************************************/
33 
34 #include "downloadmanager.h"
35 
36 #include "autosaver.h"
37 #include "browserapplication.h"
38 #include "networkaccessmanager.h"
39 
40 #include <math.h>
41 
42 #include <QtCore/QMetaEnum>
43 #include <QtCore/QSettings>
44 
45 #include <QtGui/QDesktopServices>
46 #include <QtWidgets/QFileDialog>
47 #include <QtWidgets/QHeaderView>
48 #include <QtWidgets/QFileIconProvider>
49 
50 #include <QtCore/QDebug>
51 
52 #include <QWebSettings>
53 
54 /*!
55     DownloadItem is a widget that is displayed in the download manager list.
56     It moves the data from the QNetworkReply into the QFile as well
57     as update the information/progressbar and report errors.
58  */
DownloadItem(QNetworkReply * reply,bool requestFileName,QWidget * parent)59 DownloadItem::DownloadItem(QNetworkReply *reply, bool requestFileName, QWidget *parent)
60     : QWidget(parent)
61     , m_reply(reply)
62     , m_requestFileName(requestFileName)
63     , m_bytesReceived(0)
64 {
65     setupUi(this);
66     QPalette p = downloadInfoLabel->palette();
67     p.setColor(QPalette::Text, Qt::darkGray);
68     downloadInfoLabel->setPalette(p);
69     progressBar->setMaximum(0);
70     tryAgainButton->hide();
71     connect(stopButton, SIGNAL(clicked()), this, SLOT(stop()));
72     connect(openButton, SIGNAL(clicked()), this, SLOT(open()));
73     connect(tryAgainButton, SIGNAL(clicked()), this, SLOT(tryAgain()));
74 
75     init();
76 }
77 
init()78 void DownloadItem::init()
79 {
80     if (!m_reply)
81         return;
82 
83     // attach to the m_reply
84     m_url = m_reply->url();
85     m_reply->setParent(this);
86     connect(m_reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
87     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
88             this, SLOT(error(QNetworkReply::NetworkError)));
89     connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)),
90             this, SLOT(downloadProgress(qint64,qint64)));
91     connect(m_reply, SIGNAL(metaDataChanged()),
92             this, SLOT(metaDataChanged()));
93     connect(m_reply, SIGNAL(finished()),
94             this, SLOT(finished()));
95 
96     // reset info
97     downloadInfoLabel->clear();
98     progressBar->setValue(0);
99     getFileName();
100 
101     // start timer for the download estimation
102     m_downloadTime.start();
103 
104     if (m_reply->error() != QNetworkReply::NoError) {
105         error(m_reply->error());
106         finished();
107     }
108 }
109 
getFileName()110 void DownloadItem::getFileName()
111 {
112     QSettings settings;
113     settings.beginGroup(QLatin1String("downloadmanager"));
114     QString defaultLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
115     QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), defaultLocation).toString();
116     if (!downloadDirectory.isEmpty())
117         downloadDirectory += QLatin1Char('/');
118 
119     QString defaultFileName = saveFileName(downloadDirectory);
120     QString fileName = defaultFileName;
121     if (m_requestFileName) {
122         fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName);
123         if (fileName.isEmpty()) {
124             m_reply->close();
125             fileNameLabel->setText(tr("Download canceled: %1").arg(QFileInfo(defaultFileName).fileName()));
126             return;
127         }
128     }
129     m_output.setFileName(fileName);
130     fileNameLabel->setText(QFileInfo(m_output.fileName()).fileName());
131     if (m_requestFileName)
132         downloadReadyRead();
133 }
134 
saveFileName(const QString & directory) const135 QString DownloadItem::saveFileName(const QString &directory) const
136 {
137     // Move this function into QNetworkReply to also get file name sent from the server
138     QString path = m_url.path();
139     QFileInfo info(path);
140     QString baseName = info.completeBaseName();
141     QString endName = info.suffix();
142 
143     if (baseName.isEmpty()) {
144         baseName = QLatin1String("unnamed_download");
145         qDebug() << "DownloadManager:: downloading unknown file:" << m_url;
146     }
147     QString name = directory + baseName + QLatin1Char('.') + endName;
148     if (QFile::exists(name)) {
149         // already exists, don't overwrite
150         int i = 1;
151         do {
152             name = directory + baseName + QLatin1Char('-') + QString::number(i++) + QLatin1Char('.') + endName;
153         } while (QFile::exists(name));
154     }
155     return name;
156 }
157 
158 
stop()159 void DownloadItem::stop()
160 {
161     setUpdatesEnabled(false);
162     stopButton->setEnabled(false);
163     stopButton->hide();
164     tryAgainButton->setEnabled(true);
165     tryAgainButton->show();
166     setUpdatesEnabled(true);
167     m_reply->abort();
168 }
169 
open()170 void DownloadItem::open()
171 {
172     QFileInfo info(m_output);
173     QUrl url = QUrl::fromLocalFile(info.absolutePath());
174     QDesktopServices::openUrl(url);
175 }
176 
tryAgain()177 void DownloadItem::tryAgain()
178 {
179     if (!tryAgainButton->isEnabled())
180         return;
181 
182     tryAgainButton->setEnabled(false);
183     tryAgainButton->setVisible(false);
184     stopButton->setEnabled(true);
185     stopButton->setVisible(true);
186     progressBar->setVisible(true);
187 
188     QNetworkReply *r = BrowserApplication::networkAccessManager()->get(QNetworkRequest(m_url));
189     if (m_reply)
190         m_reply->deleteLater();
191     if (m_output.exists())
192         m_output.remove();
193     m_reply = r;
194     init();
195     emit statusChanged();
196 }
197 
downloadReadyRead()198 void DownloadItem::downloadReadyRead()
199 {
200     if (m_requestFileName && m_output.fileName().isEmpty())
201         return;
202     if (!m_output.isOpen()) {
203         // in case someone else has already put a file there
204         if (!m_requestFileName)
205             getFileName();
206         if (!m_output.open(QIODevice::WriteOnly)) {
207             downloadInfoLabel->setText(tr("Error opening save file: %1")
208                     .arg(m_output.errorString()));
209             stopButton->click();
210             emit statusChanged();
211             return;
212         }
213         emit statusChanged();
214     }
215     if (-1 == m_output.write(m_reply->readAll())) {
216         downloadInfoLabel->setText(tr("Error saving: %1")
217                 .arg(m_output.errorString()));
218         stopButton->click();
219     }
220 }
221 
error(QNetworkReply::NetworkError)222 void DownloadItem::error(QNetworkReply::NetworkError)
223 {
224     qDebug() << "DownloadItem::error" << m_reply->errorString() << m_url;
225     downloadInfoLabel->setText(tr("Network Error: %1").arg(m_reply->errorString()));
226     tryAgainButton->setEnabled(true);
227     tryAgainButton->setVisible(true);
228 }
229 
metaDataChanged()230 void DownloadItem::metaDataChanged()
231 {
232     qDebug() << "DownloadItem::metaDataChanged: not handled.";
233 }
234 
downloadProgress(qint64 bytesReceived,qint64 bytesTotal)235 void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
236 {
237     m_bytesReceived = bytesReceived;
238     if (bytesTotal == -1) {
239         progressBar->setValue(0);
240         progressBar->setMaximum(0);
241     } else {
242         progressBar->setValue(bytesReceived);
243         progressBar->setMaximum(bytesTotal);
244     }
245     updateInfoLabel();
246 }
247 
updateInfoLabel()248 void DownloadItem::updateInfoLabel()
249 {
250     if (m_reply->error() == QNetworkReply::NoError)
251         return;
252 
253     qint64 bytesTotal = progressBar->maximum();
254     bool running = !downloadedSuccessfully();
255 
256     // update info label
257     double speed = m_bytesReceived * 1000.0 / m_downloadTime.elapsed();
258     double timeRemaining = ((double)(bytesTotal - m_bytesReceived)) / speed;
259     QString timeRemainingString = tr("seconds");
260     if (timeRemaining > 60) {
261         timeRemaining = timeRemaining / 60;
262         timeRemainingString = tr("minutes");
263     }
264     timeRemaining = floor(timeRemaining);
265 
266     // When downloading the eta should never be 0
267     if (timeRemaining == 0)
268         timeRemaining = 1;
269 
270     QString info;
271     if (running) {
272         QString remaining;
273         if (bytesTotal != 0)
274             remaining = tr("- %4 %5 remaining")
275             .arg(timeRemaining)
276             .arg(timeRemainingString);
277         info = tr("%1 of %2 (%3/sec) %4")
278             .arg(dataString(m_bytesReceived))
279             .arg(bytesTotal == 0 ? tr("?") : dataString(bytesTotal))
280             .arg(dataString((int)speed))
281             .arg(remaining);
282     } else {
283         if (m_bytesReceived == bytesTotal)
284             info = dataString(m_output.size());
285         else
286             info = tr("%1 of %2 - Stopped")
287                 .arg(dataString(m_bytesReceived))
288                 .arg(dataString(bytesTotal));
289     }
290     downloadInfoLabel->setText(info);
291 }
292 
dataString(int size) const293 QString DownloadItem::dataString(int size) const
294 {
295     QString unit;
296     if (size < 1024) {
297         unit = tr("bytes");
298     } else if (size < 1024*1024) {
299         size /= 1024;
300         unit = tr("kB");
301     } else {
302         size /= 1024*1024;
303         unit = tr("MB");
304     }
305     return QString(QLatin1String("%1 %2")).arg(size).arg(unit);
306 }
307 
downloading() const308 bool DownloadItem::downloading() const
309 {
310     return (progressBar->isVisible());
311 }
312 
downloadedSuccessfully() const313 bool DownloadItem::downloadedSuccessfully() const
314 {
315     return (stopButton->isHidden() && tryAgainButton->isHidden());
316 }
317 
finished()318 void DownloadItem::finished()
319 {
320     progressBar->hide();
321     stopButton->setEnabled(false);
322     stopButton->hide();
323     m_output.close();
324     updateInfoLabel();
325     emit statusChanged();
326 }
327 
328 /*!
329     DownloadManager is a Dialog that contains a list of DownloadItems
330 
331     It is a basic download manager.  It only downloads the file, doesn't do BitTorrent,
332     extract zipped files or anything fancy.
333   */
DownloadManager(QWidget * parent)334 DownloadManager::DownloadManager(QWidget *parent)
335     : QDialog(parent)
336     , m_autoSaver(new AutoSaver(this))
337     , m_manager(BrowserApplication::networkAccessManager())
338     , m_iconProvider(0)
339     , m_removePolicy(Never)
340 {
341     setupUi(this);
342     downloadsView->setShowGrid(false);
343     downloadsView->verticalHeader()->hide();
344     downloadsView->horizontalHeader()->hide();
345     downloadsView->setAlternatingRowColors(true);
346     downloadsView->horizontalHeader()->setStretchLastSection(true);
347     m_model = new DownloadModel(this);
348     downloadsView->setModel(m_model);
349     connect(cleanupButton, SIGNAL(clicked()), this, SLOT(cleanup()));
350     load();
351 }
352 
~DownloadManager()353 DownloadManager::~DownloadManager()
354 {
355     m_autoSaver->changeOccurred();
356     m_autoSaver->saveIfNeccessary();
357     if (m_iconProvider)
358         delete m_iconProvider;
359 }
360 
activeDownloads() const361 int DownloadManager::activeDownloads() const
362 {
363     int count = 0;
364     for (int i = 0; i < m_downloads.count(); ++i) {
365         if (m_downloads.at(i)->stopButton->isEnabled())
366             ++count;
367     }
368     return count;
369 }
370 
download(const QNetworkRequest & request,bool requestFileName)371 void DownloadManager::download(const QNetworkRequest &request, bool requestFileName)
372 {
373     if (request.url().isEmpty())
374         return;
375     handleUnsupportedContent(m_manager->get(request), requestFileName);
376 }
377 
handleUnsupportedContent(QNetworkReply * reply,bool requestFileName)378 void DownloadManager::handleUnsupportedContent(QNetworkReply *reply, bool requestFileName)
379 {
380     if (!reply || reply->url().isEmpty())
381         return;
382     QVariant header = reply->header(QNetworkRequest::ContentLengthHeader);
383     bool ok;
384     int size = header.toInt(&ok);
385     if (ok && size == 0)
386         return;
387 
388     qDebug() << "DownloadManager::handleUnsupportedContent" << reply->url() << "requestFileName" << requestFileName;
389     DownloadItem *item = new DownloadItem(reply, requestFileName, this);
390     addItem(item);
391 }
392 
addItem(DownloadItem * item)393 void DownloadManager::addItem(DownloadItem *item)
394 {
395     connect(item, SIGNAL(statusChanged()), this, SLOT(updateRow()));
396     int row = m_downloads.count();
397     m_model->beginInsertRows(QModelIndex(), row, row);
398     m_downloads.append(item);
399     m_model->endInsertRows();
400     updateItemCount();
401     if (row == 0)
402         show();
403     downloadsView->setIndexWidget(m_model->index(row, 0), item);
404     QIcon icon = style()->standardIcon(QStyle::SP_FileIcon);
405     item->fileIcon->setPixmap(icon.pixmap(48, 48));
406     downloadsView->setRowHeight(row, item->sizeHint().height());
407 }
408 
updateRow()409 void DownloadManager::updateRow()
410 {
411     DownloadItem *item = qobject_cast<DownloadItem*>(sender());
412     int row = m_downloads.indexOf(item);
413     if (-1 == row)
414         return;
415     if (!m_iconProvider)
416         m_iconProvider = new QFileIconProvider();
417     QIcon icon = m_iconProvider->icon(item->m_output.fileName());
418     if (icon.isNull())
419         icon = style()->standardIcon(QStyle::SP_FileIcon);
420     item->fileIcon->setPixmap(icon.pixmap(48, 48));
421     downloadsView->setRowHeight(row, item->minimumSizeHint().height());
422 
423     bool remove = false;
424     QWebSettings *globalSettings = QWebSettings::globalSettings();
425     if (!item->downloading()
426         && globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled))
427         remove = true;
428 
429     if (item->downloadedSuccessfully()
430         && removePolicy() == DownloadManager::SuccessFullDownload) {
431         remove = true;
432     }
433     if (remove)
434         m_model->removeRow(row);
435 
436     cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);
437 }
438 
removePolicy() const439 DownloadManager::RemovePolicy DownloadManager::removePolicy() const
440 {
441     return m_removePolicy;
442 }
443 
setRemovePolicy(RemovePolicy policy)444 void DownloadManager::setRemovePolicy(RemovePolicy policy)
445 {
446     if (policy == m_removePolicy)
447         return;
448     m_removePolicy = policy;
449     m_autoSaver->changeOccurred();
450 }
451 
save() const452 void DownloadManager::save() const
453 {
454     QSettings settings;
455     settings.beginGroup(QLatin1String("downloadmanager"));
456     QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));
457     settings.setValue(QLatin1String("removeDownloadsPolicy"), QLatin1String(removePolicyEnum.valueToKey(m_removePolicy)));
458     settings.setValue(QLatin1String("size"), size());
459     if (m_removePolicy == Exit)
460         return;
461 
462     for (int i = 0; i < m_downloads.count(); ++i) {
463         QString key = QString(QLatin1String("download_%1_")).arg(i);
464         settings.setValue(key + QLatin1String("url"), m_downloads[i]->m_url);
465         settings.setValue(key + QLatin1String("location"), QFileInfo(m_downloads[i]->m_output).filePath());
466         settings.setValue(key + QLatin1String("done"), m_downloads[i]->downloadedSuccessfully());
467     }
468     int i = m_downloads.count();
469     QString key = QString(QLatin1String("download_%1_")).arg(i);
470     while (settings.contains(key + QLatin1String("url"))) {
471         settings.remove(key + QLatin1String("url"));
472         settings.remove(key + QLatin1String("location"));
473         settings.remove(key + QLatin1String("done"));
474         key = QString(QLatin1String("download_%1_")).arg(++i);
475     }
476 }
477 
load()478 void DownloadManager::load()
479 {
480     QSettings settings;
481     settings.beginGroup(QLatin1String("downloadmanager"));
482     QSize size = settings.value(QLatin1String("size")).toSize();
483     if (size.isValid())
484         resize(size);
485     QByteArray value = settings.value(QLatin1String("removeDownloadsPolicy"), QLatin1String("Never")).toByteArray();
486     QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));
487     m_removePolicy = removePolicyEnum.keyToValue(value) == -1 ?
488                         Never :
489                         static_cast<RemovePolicy>(removePolicyEnum.keyToValue(value));
490 
491     int i = 0;
492     QString key = QString(QLatin1String("download_%1_")).arg(i);
493     while (settings.contains(key + QLatin1String("url"))) {
494         QUrl url = settings.value(key + QLatin1String("url")).toUrl();
495         QString fileName = settings.value(key + QLatin1String("location")).toString();
496         bool done = settings.value(key + QLatin1String("done"), true).toBool();
497         if (!url.isEmpty() && !fileName.isEmpty()) {
498             DownloadItem *item = new DownloadItem(0, this);
499             item->m_output.setFileName(fileName);
500             item->fileNameLabel->setText(QFileInfo(item->m_output.fileName()).fileName());
501             item->m_url = url;
502             item->stopButton->setVisible(false);
503             item->stopButton->setEnabled(false);
504             item->tryAgainButton->setVisible(!done);
505             item->tryAgainButton->setEnabled(!done);
506             item->progressBar->setVisible(!done);
507             addItem(item);
508         }
509         key = QString(QLatin1String("download_%1_")).arg(++i);
510     }
511     cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);
512 }
513 
cleanup()514 void DownloadManager::cleanup()
515 {
516     if (m_downloads.isEmpty())
517         return;
518     m_model->removeRows(0, m_downloads.count());
519     updateItemCount();
520     if (m_downloads.isEmpty() && m_iconProvider) {
521         delete m_iconProvider;
522         m_iconProvider = 0;
523     }
524     m_autoSaver->changeOccurred();
525 }
526 
updateItemCount()527 void DownloadManager::updateItemCount()
528 {
529     int count = m_downloads.count();
530     itemCount->setText(count == 1 ? tr("1 Download") : tr("%1 Downloads").arg(count));
531 }
532 
DownloadModel(DownloadManager * downloadManager,QObject * parent)533 DownloadModel::DownloadModel(DownloadManager *downloadManager, QObject *parent)
534     : QAbstractListModel(parent)
535     , m_downloadManager(downloadManager)
536 {
537 }
538 
data(const QModelIndex & index,int role) const539 QVariant DownloadModel::data(const QModelIndex &index, int role) const
540 {
541     if (index.row() < 0 || index.row() >= rowCount(index.parent()))
542         return QVariant();
543     if (role == Qt::ToolTipRole)
544         if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully())
545             return m_downloadManager->m_downloads.at(index.row())->downloadInfoLabel->text();
546     return QVariant();
547 }
548 
rowCount(const QModelIndex & parent) const549 int DownloadModel::rowCount(const QModelIndex &parent) const
550 {
551     return (parent.isValid()) ? 0 : m_downloadManager->m_downloads.count();
552 }
553 
removeRows(int row,int count,const QModelIndex & parent)554 bool DownloadModel::removeRows(int row, int count, const QModelIndex &parent)
555 {
556     if (parent.isValid())
557         return false;
558 
559     int lastRow = row + count - 1;
560     for (int i = lastRow; i >= row; --i) {
561         if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully()
562             || m_downloadManager->m_downloads.at(i)->tryAgainButton->isEnabled()) {
563             beginRemoveRows(parent, i, i);
564             m_downloadManager->m_downloads.takeAt(i)->deleteLater();
565             endRemoveRows();
566         }
567     }
568     m_downloadManager->m_autoSaver->changeOccurred();
569     return true;
570 }
571 
572