Commit 4c5e3972 authored by Marko Mattila's avatar Marko Mattila

[transfer-engine] Introduces TransferModel QML component. Cleaning the database from failed syncs.

Removed references to files, which are not in git.
parent f3731ef2
TEMPLATE = lib
QT += dbus sql
CONFIG += qt plugin
INCLUDEPATH += ../lib
LIBS += -lnemotransferengine-qt5 -L../lib
TARGET = declarativetransferengine
QT += qml
uri = org.nemomobile.transferengine
# Input
SOURCES += \
plugin.cpp \
declarativetransfermodel.cpp
HEADERS += \
synchronizelists_p.h \
declarativetransfermodel.h
OTHER_FILES = qmldir *.qml *.js
!equals(_PRO_FILE_PWD_, $$OUT_PWD) {
copy_qmldir.target = $$OUT_PWD/qmldir
copy_qmldir.depends = $$_PRO_FILE_PWD_/qmldir
copy_qmldir.commands = $(COPY_FILE) \"$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)\"
QMAKE_EXTRA_TARGETS += copy_qmldir
PRE_TARGETDEPS += $$copy_qmldir.target
}
target.path = $$[QT_INSTALL_QML]/$$replace(uri, \\., /)
qmldir.files = qmldir *.qml *.js
qmldir.path = $$target.path
INSTALLS += target qmldir
This diff is collapsed.
/****************************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Marko Mattila <marko.mattila@jolla.com>
** All rights reserved.
**
** This file is part of Nemo Transfer Engine package.
**
** You may use this file under the terms of the GNU Lesser General
** Public License version 2.1 as published by the Free Software Foundation
** and appearing in the file license.lgpl included in the packaging
** of this file.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file license.lgpl included in the packaging
** of this file.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
****************************************************************************************/
#ifndef DECLARATIVETRANSFERMODEL_HH
#define DECLARATIVETRANSFERMODEL_HH
#include <QAbstractListModel>
#include <QJSValue>
#include <QMutex>
#include <QQmlParserStatus>
#include <QRunnable>
#include <QStringList>
#include <QWaitCondition>
#include <QSqlDatabase>
#include "transferengineinterface.h"
#include "transfertypes.h"
class TransferModel
: public QAbstractListModel
, public QQmlParserStatus
, private QRunnable
{
Q_OBJECT
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
Q_PROPERTY(int transfersInProgress READ transfersInProgress NOTIFY transfersInProgressChanged)
Q_ENUMS(Status)
Q_ENUMS(TransferStatus)
Q_ENUMS(TransferType)
Q_INTERFACES(QQmlParserStatus)
public:
enum Status {
Null,
Querying,
Finished,
Error
};
enum TransferStatus {
NotStarted = TransferEngineData::NotStarted,
TransferStarted = TransferEngineData::TransferStarted,
TransferCanceled = TransferEngineData::TransferCanceled,
TransferFinished = TransferEngineData::TransferFinished,
TransferInterrupted = TransferEngineData::TransferInterrupted
};
enum TransferType {
Upload = TransferEngineData::Upload,
Download = TransferEngineData::Download,
Sync = TransferEngineData::Sync
};
TransferModel(QObject *parent = 0);
~TransferModel();
Status status() const;
Q_INVOKABLE QJSValue get(int index) const;
Q_INVOKABLE void clearTransfers();
QHash<int, QByteArray> roleNames() const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
void classBegin();
void componentComplete();
void insertRange(int index, int count, const QVector<TransferDBRecord> &source, int sourceIndex);
void updateRange(int index, int count, const QVector<TransferDBRecord> &source, int sourceIndex);
void removeRange(int index, int count);
bool event(QEvent *event);
int transfersInProgress() const;
public slots:
void refresh();
signals:
void queryChanged();
void classNamesChanged();
void statusChanged();
void countChanged();
void transfersInProgressChanged();
private:
void run();
bool executeQuery(QVector<TransferDBRecord> *rows, int *activeTransfers, QString *errorString);
QSqlDatabase database();
QString m_asyncErrorString;
QVector<TransferDBRecord> m_asyncRows;
QVector<TransferDBRecord> *m_rows;
QHash<int, QByteArray> m_roles;
QMutex m_mutex;
QWaitCondition m_condition;
int m_rowsIndex;
int m_asyncIndex;
int m_asyncTransfersInProgress;
int m_transfersInProgress;
Status m_status;
Status m_asyncStatus;
bool m_asyncPending;
bool m_asyncRunning;
bool m_notified;
bool m_complete;
bool m_rowsChanges;
TransferEngineInterface *m_client;
};
#endif
/****************************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Marko Mattila <marko.mattila@jolla.com>
** All rights reserved.
**
** This file is part of Nemo Transfer Engine package.
**
** You may use this file under the terms of the GNU Lesser General
** Public License version 2.1 as published by the Free Software Foundation
** and appearing in the file license.lgpl included in the packaging
** of this file.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file license.lgpl included in the packaging
** of this file.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
****************************************************************************************/
#include <QtGlobal>
#include <QtQml>
#include <QQmlEngine>
#include <QQmlExtensionPlugin>
#include "declarativetransfermodel.h"
class Q_DECL_EXPORT DeclarativePlugin: public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.nemomobile.transferengine")
public:
void initializeEngine(QQmlEngine *engine, const char *uri)
{
Q_UNUSED(engine)
Q_UNUSED(uri)
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.nemomobile.transferengine"));
}
void registerTypes(const char *uri)
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.nemomobile.transferengine"));
qmlRegisterType<TransferModel>(uri, 1, 0, "TransferModel");
}
};
#include "plugin.moc"
module org.nemomobile.transferengine
plugin declarativetransferengine
/****************************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Andrew den Exter <andrew.den.exter@jolla.com>
** All rights reserved.
**
** This file is part of Nemo Transfer Engine package.
**
** You may use this file under the terms of the GNU Lesser General
** Public License version 2.1 as published by the Free Software Foundation
** and appearing in the file license.lgpl included in the packaging
** of this file.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file license.lgpl included in the packaging
** of this file.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
****************************************************************************************/
#ifndef SYNCHRONIZELISTS_P_H
#define SYNCHRONIZELISTS_P_H
template <typename T>
bool compareIdentity(const T &item, const T &reference)
{
return item == reference;
}
template <typename Agent, typename ReferenceList>
int insertRange(Agent *agent, int index, int count, const ReferenceList &source, int sourceIndex)
{
agent->insertRange(index, count, source, sourceIndex);
return count;
}
template <typename Agent>
int removeRange(Agent *agent, int index, int count)
{
agent->removeRange(index, count);
return 0;
}
template <typename Agent, typename ReferenceList>
int updateRange(Agent *agent, int index, int count, const ReferenceList &source, int sourceIndex)
{
agent->updateRange(index, count, source, sourceIndex);
return count;
}
template <typename Agent, typename CacheList, typename ReferenceList>
class SynchronizeList
{
public:
SynchronizeList(
Agent *agent,
const CacheList &cache,
int &c,
const ReferenceList &reference,
int &r)
: agent(agent), cache(cache), c(c), reference(reference), r(r)
{
int lastEqualC = c;
int lastEqualR = r;
for (; c < cache.count() && r < reference.count(); ++c, ++r) {
if (compareIdentity(cache.at(c), reference.at(r))) {
continue;
}
if (c > lastEqualC) {
lastEqualC += updateRange(agent, lastEqualC, c - lastEqualC, reference, lastEqualR);
c = lastEqualC;
lastEqualR = r;
}
bool match = false;
// Iterate through both the reference and cache lists in parallel looking for first
// point of commonality, when that is found resolve the differences and continue
// looking.
int count = 1;
for (; !match && c + count < cache.count() && r + count < reference.count(); ++count) {
typename CacheList::const_reference cacheItem = cache.at(c + count);
typename ReferenceList::const_reference referenceItem = reference.at(r + count);
for (int i = 0; i <= count; ++i) {
if (cacheMatch(i, count, referenceItem) || referenceMatch(i, count, cacheItem)) {
match = true;
break;
}
}
}
// Continue scanning the reference list if the cache has been exhausted.
for (int re = r + count; !match && re < reference.count(); ++re) {
typename ReferenceList::const_reference referenceItem = reference.at(re);
for (int i = 0; i < count; ++i) {
if (cacheMatch(i, re - r, referenceItem)) {
match = true;
break;
}
}
}
// Continue scanning the cache if the reference list has been exhausted.
for (int ce = c + count; !match && ce < cache.count(); ++ce) {
typename CacheList::const_reference cacheItem = cache.at(ce);
for (int i = 0; i < count; ++i) {
if (referenceMatch(i, ce - c, cacheItem)) {
match = true;
break;
}
}
}
if (!match)
return;
lastEqualC = c;
lastEqualR = r;
}
if (c > lastEqualC) {
updateRange(agent, lastEqualC, c - lastEqualC, reference, lastEqualR);
}
}
private:
// Tests if the cached id at i matches a referenceId.
// If there is a match removes all items traversed in the cache since the previous match
// and inserts any items in the reference set found to to not be in the cache.
bool cacheMatch(int i, int count, typename ReferenceList::const_reference referenceItem)
{
if (compareIdentity(cache.at(c + i), referenceItem)) {
if (i > 0)
c += removeRange(agent, c, i);
c += insertRange(agent, c, count, reference, r);
r += count;
return true;
} else {
return false;
}
}
// Tests if the referenceid at i matches a cacheId.
// If there is a match inserts all items traversed in the reference set since the
// previous match and removes any items from the cache that were not found in the
// reference list.
bool referenceMatch(int i, int count, typename ReferenceList::const_reference cacheItem)
{
if (compareIdentity(reference.at(r + i), cacheItem)) {
c += removeRange(agent, c, count);
if (i > 0)
c += insertRange(agent, c, i, reference, r);
r += i;
return true;
} else {
return false;
}
}
Agent * const agent;
const CacheList &cache;
int &c;
const ReferenceList &reference;
int &r;
};
template <typename Agent, typename CacheList, typename ReferenceList>
void completeSynchronizeList(
Agent *agent,
const CacheList &cache,
int &cacheIndex,
const ReferenceList &reference,
int &referenceIndex)
{
if (cacheIndex < cache.count()) {
agent->removeRange(cacheIndex, cache.count() - cacheIndex);
}
if (referenceIndex < reference.count()) {
agent->insertRange(cache.count(), reference.count() - referenceIndex, reference, referenceIndex);
}
cacheIndex = 0;
referenceIndex = 0;
}
template <typename Agent, typename CacheList, typename ReferenceList>
void synchronizeList(
Agent *agent,
const CacheList &cache,
int &cacheIndex,
const ReferenceList &reference,
int &referenceIndex)
{
SynchronizeList<Agent, CacheList, ReferenceList>(
agent, cache, cacheIndex, reference, referenceIndex);
}
template <typename Agent, typename CacheList, typename ReferenceList>
void synchronizeList(Agent *agent, const CacheList &cache, const ReferenceList &reference)
{
int cacheIndex = 0;
int referenceIndex = 0;
SynchronizeList<Agent, CacheList, ReferenceList>(
agent, cache, cacheIndex, reference, referenceIndex);
completeSynchronizeList(agent, cache, cacheIndex, reference, referenceIndex);
}
#endif
......@@ -248,6 +248,29 @@ QVariant TransferDBRecord::value(int index) const
return restart_supported;
default:
qWarning() << Q_FUNC_INFO << "Unknown index: " << index;
return QVariant();
}
}
bool TransferDBRecord::isValid() const
{
return transfer_id > 0 && transfer_type > 0;
}
bool operator ==(const TransferDBRecord &left, const TransferDBRecord &right)
{
return left.transfer_id == right.transfer_id &&
left.transfer_type == right.transfer_type &&
left.status == right.status &&
left.progress == right.progress;
}
bool operator !=(const TransferDBRecord &left, const TransferDBRecord &right)
{
return left.transfer_id != right.transfer_id ||
left.transfer_type != right.transfer_type ||
left.status != right.status ||
left.progress != right.progress;
}
......@@ -67,6 +67,12 @@ public:
QVariant value(int index) const;
bool isValid() const;
friend bool operator ==(const TransferDBRecord &left, const TransferDBRecord &right);
friend bool operator !=(const TransferDBRecord &left, const TransferDBRecord &right);
// TODO: Maybe this could use QVariantList internally to ease of pain of keeping thigs up to date
// when database structure / fields change
int transfer_id;
......@@ -87,6 +93,9 @@ public:
bool restart_supported;
};
bool operator ==(const TransferDBRecord &left, const TransferDBRecord &right);
bool operator !=(const TransferDBRecord &left, const TransferDBRecord &right);
#endif // DBUSTYPES_H
Name: nemo-transferengine-qt5
Version: 0.0.29
Version: 0.0.30
Release: 0
Summary: Transfer Engine for uploading media content and tracking transfers.
Group: System Environment/Daemon
......@@ -33,7 +33,6 @@ Obsoletes: nemo-transferengine <= 0.0.19
%{_datadir}/translations/nemo-transfer-engine_eng_en.qm
%package -n libnemotransferengine-qt5
Summary: Transfer engine library.
Group: Development/Libraries
......@@ -44,6 +43,7 @@ Group: Development/Libraries
%files -n libnemotransferengine-qt5
%defattr(-,root,root,-)
%{_libdir}/*.so.*
%{_libdir}/qt5/qml/org/nemomobile/transferengine/*
%package -n libnemotransferengine-qt5-devel
Summary: Development headers for transfer engine library.
......
......@@ -557,7 +557,9 @@ bool DbManager::removeTransfer(int key)
QSqlQuery query;
if (!query.exec(queryStr)) {
qWarning() << "Failed to execute SQL query. Couldn't delete the transfer with key " << key;
qWarning() << Q_FUNC_INFO;
qWarning() << "Failed to execute SQL query: " << query.lastQuery();
qWarning() << query.lastError().text();
return false;
}
......@@ -565,6 +567,32 @@ bool DbManager::removeTransfer(int key)
return true;
}
/*!
Remove failed transfers with same provider except the one having the \a excludeKey. Argument \a type defines the type
of the removed failed transfers and can be one of TransferEngineData::Download, TransferEngineData::Upload
or TransferEngineData::Sync.
This methods returns true on success or false on failure
*/
bool DbManager::clearFailedTransfers(int excludeKey, TransferEngineData::TransferType type)
{
// DELETE FROM transfers where transfer_id!=4584 AND status=5 AND display_name=(SELECT display_name FROM transfers WHERE transfer_id=4584);
QString queryStr = QString("DELETE FROM transfers WHERE transfer_id!=%1 AND status=%2 AND transfer_type=%3 AND display_name=(SELECT display_name FROM transfers WHERE transfer_id=%1);")
.arg(excludeKey)
.arg(TransferEngineData::TransferInterrupted)
.arg(type);
QSqlQuery query;
if (!query.exec(queryStr)) {
qWarning() << Q_FUNC_INFO;
qWarning() << "Failed to execute query: " << query.lastQuery();
qWarning() << query.lastError().text();
return false;
}
query.finish();
return true;
}
/*!
Clears all finished, canceled or failed transfers from the database.
......
......@@ -59,6 +59,8 @@ public:
bool removeTransfer(int key);
bool clearFailedTransfers(int excludeKey, TransferEngineData::TransferType type);
bool clearTransfers();
QList<TransferDBRecord> transfers() const;
......
......@@ -656,7 +656,7 @@ void TransferEnginePrivate::pluginInfoError(const QString &msg)
qWarning() << "TransferEnginePrivate::pluginInfoError:" << msg;
TransferPluginInfo *infoObj = qobject_cast<TransferPluginInfo*>(sender());
m_infoObjects.removeOne(infoObj);
delete infoObj;
infoObj->deleteLater();
if (m_infoObjects.isEmpty()) {
Q_Q(TransferEngine);
......@@ -1152,17 +1152,30 @@ void TransferEngine::finishTransfer(int transferId, int status, const QString &r
transferStatus == TransferEngineData::TransferCanceled ||
transferStatus == TransferEngineData::TransferInterrupted) {
DbManager::instance()->updateTransferStatus(transferId, transferStatus);
d->m_activityMonitor->activityFinished(transferId);
d->sendNotification(type, transferStatus, fileName);
emit statusChanged(transferId, status);
// We don't want to leave successfully finished syncs to populate the database, just remove it.
if (type == TransferEngineData::Sync &&
transferStatus == TransferEngineData::TransferFinished) {
if (DbManager::instance()->removeTransfer(transferId)) {
emit transfersChanged();
bool notify = false;
// Clean up old failed syncs from the database and leave only the latest one there
if (type == TransferEngineData::Sync) {
if (DbManager::instance()->clearFailedTransfers(transferId, type)) {
notify = true;
}
// We don't want to leave successfully finished syncs to populate the database, just remove it.
if (transferStatus == TransferEngineData::TransferFinished) {
if (DbManager::instance()->removeTransfer(transferId)) {
notify = true;
}
}
}
if (notify) {
emit transfersChanged();
}
d->m_activityMonitor->activityFinished(transferId);
}
}
......
TEMPLATE = subdirs
SUBDIRS = lib src tests
SUBDIRS = lib src declarative tests
src.depends = lib
tests.depends = lib
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment