Skip to content

Latest commit

 

History

History
1308 lines (1114 loc) · 48.9 KB

transferengine.cpp

File metadata and controls

1308 lines (1114 loc) · 48.9 KB
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/****************************************************************************************
**
** Copyright (C) 2013 Jolla Ltd.
** Contact: Marko Mattila <marko.mattila@jollamobile.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 "transferengine.h"
#include "transferengine_p.h"
#include "transferplugininterface.h"
#include "mediaitem.h"
#include "dbmanager.h"
#include "transferengineadaptor.h"
#include "transfertypes.h"
#include "transferplugininfo.h"
#include <QDir>
#include <QtDebug>
#include <QPluginLoader>
#include <QDBusMessage>
#include <QFileSystemWatcher>
#include <QTimer>
#include <MNotification>
#include <MNotificationGroup>
#include <signal.h>
Nov 22, 2013
Nov 22, 2013
48
49
#include <Accounts/Manager>
50
#define SHARE_PLUGINS_PATH "/usr/lib/nemo-transferengine/plugins"
Apr 16, 2013
Apr 16, 2013
51
#define CONFIG_PATH "/usr/share/nemo-transferengine/nemo-transfer-engine.conf"
52
#define FILE_WATCHER_TIMEOUT 5000
Jul 1, 2013
Jul 1, 2013
53
#define ACTIVITY_MONITOR_TIMEOUT 1*60*1000 // 1 minute in ms
Jul 1, 2013
Jul 1, 2013
54
#define TRANSFER_EXPIRATION_THRESHOLD 3*60 // 3 minutes in seconds
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
TransferEngineSignalHandler * TransferEngineSignalHandler::instance()
{
static TransferEngineSignalHandler instance;
return &instance;
}
void TransferEngineSignalHandler::signalHandler(int signal)
{
if(signal == SIGUSR1) {
QMetaObject::invokeMethod(TransferEngineSignalHandler::instance(),
"exitSafely",
Qt::AutoConnection);
}
}
TransferEngineSignalHandler::TransferEngineSignalHandler()
{
signal(SIGUSR1, TransferEngineSignalHandler::signalHandler);
}
Jul 1, 2013
Jul 1, 2013
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// ---------------------------
// ClientActivityMonitor runs periodic checks if there are transfers which are expired.
// A transfer can be expired e.g. when a client has been crashed in the middle of Sync,
// Download or Upload operation or the client API isn't used properly.
//
// NOTE: This class only monitors if there are expired transfers and emit signal to indicate
// that it's cleaning time. It is up to Transfer Engine to remoce expired ids from the
// ClientActivityMonitor instance.
ClientActivityMonitor::ClientActivityMonitor(QObject *parent)
: QObject(parent)
, m_timer(new QTimer(this))
{
connect(m_timer, SIGNAL(timeout()), this, SLOT(checkActivity()));
m_timer->start(ACTIVITY_MONITOR_TIMEOUT);
}
ClientActivityMonitor::~ClientActivityMonitor()
{
m_activityMap.clear();
}
void ClientActivityMonitor::newActivity(int transferId)
{
// Update or add a new timestamp
m_activityMap.insert(transferId, QDateTime::currentDateTimeUtc().toTime_t());
}
void ClientActivityMonitor::activityFinished(int transferId)
{
if (!m_activityMap.contains(transferId)) {
qWarning() << Q_FUNC_INFO << "Could not find matching TransferId. This is probably an error!";
return;
}
m_activityMap.remove(transferId);
}
bool ClientActivityMonitor::activeTransfers() const
{
return !m_activityMap.isEmpty();
}
void ClientActivityMonitor::checkActivity()
{
// Check if there are existing transfers which are not yet finished and
// they've been around too long. Notify TransferEngine about these transfers.
QList<int> ids;
Jul 1, 2013
Jul 1, 2013
124
quint32 currTime = QDateTime::currentDateTimeUtc().toTime_t();
Jul 1, 2013
Jul 1, 2013
125
126
127
128
129
130
131
132
133
134
135
136
137
QMap<int, quint32>::const_iterator i = m_activityMap.constBegin();
while (i != m_activityMap.constEnd()) {
if ((currTime - i.value()) >= TRANSFER_EXPIRATION_THRESHOLD) {
ids << i.key();
}
i++;
}
if (!ids.isEmpty()) {
emit transfersExpired(ids);
}
}
138
139
140
141
142
// ----------------------------
TransferEnginePrivate::TransferEnginePrivate(TransferEngine *parent):
m_notificationsEnabled(true),
Apr 16, 2013
Apr 16, 2013
143
m_settings(CONFIG_PATH, QSettings::IniFormat),
144
145
146
147
148
149
q_ptr(parent)
{
m_fileWatcherTimer = new QTimer(this);
m_fileWatcherTimer->setSingleShot(true);
connect(m_fileWatcherTimer, SIGNAL(timeout()), this, SLOT(enabledPluginsCheck()));
Sep 30, 2014
Sep 30, 2014
150
151
152
m_delayedExitTimer = new QTimer(this);
m_delayedExitTimer->setSingleShot(true);
m_delayedExitTimer->setInterval(5000);
Oct 1, 2014
Oct 1, 2014
153
m_delayedExitTimer->start(); // Exit if nothing happens within 5 sec
Sep 30, 2014
Sep 30, 2014
154
155
connect(m_delayedExitTimer, SIGNAL(timeout()), this, SLOT(delayedExitSafely()));
156
157
158
159
160
161
162
163
164
m_fileWatcher = new QFileSystemWatcher(this);
m_fileWatcher->addPath(SHARE_PLUGINS_PATH);
connect(m_fileWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(pluginDirChanged()));
m_accountManager = new Accounts::Manager("sharing", this);
connect(m_accountManager, SIGNAL(accountCreated(Accounts::AccountId)), this, SLOT(enabledPluginsCheck()));
connect(m_accountManager, SIGNAL(accountRemoved(Accounts::AccountId)), this, SLOT(enabledPluginsCheck()));
connect(m_accountManager, SIGNAL(accountUpdated(Accounts::AccountId)), this, SLOT(enabledPluginsCheck()));
Jul 1, 2013
Jul 1, 2013
165
// Exit safely stuff if we recieve certain signal or there are no active transfers
166
Q_Q(TransferEngine);
Jul 1, 2013
Jul 1, 2013
167
168
169
170
171
172
connect(TransferEngineSignalHandler::instance(), SIGNAL(exitSafely()), this, SLOT(exitSafely()));
connect(q, SIGNAL(statusChanged(int,int)), this, SLOT(exitSafely()));
// Monitor expired transfers and cleanup them if required
m_activityMonitor = new ClientActivityMonitor(this);
connect(m_activityMonitor, SIGNAL(transfersExpired(QList<int>)), this, SLOT(cleanupExpiredTransfers(QList<int>)));
173
174
175
176
177
178
179
180
181
182
183
184
}
void TransferEnginePrivate::pluginDirChanged()
{
// We need to check our plugins, but only after a short period of time
// because some operations may cause calling this slot over 10 times.
// E.g. reinstallation of plugins from the RPM package
m_fileWatcherTimer->start(FILE_WATCHER_TIMEOUT);
}
void TransferEnginePrivate::exitSafely()
{
Jul 1, 2013
Jul 1, 2013
185
if (!m_activityMonitor->activeTransfers()) {
Sep 30, 2014
Sep 30, 2014
186
187
188
189
qDebug() << "Scheduling exit in" << m_delayedExitTimer->interval() << "ms";
m_delayedExitTimer->start();
} else {
m_delayedExitTimer->stop();
Feb 21, 2014
Feb 21, 2014
190
191
192
193
194
}
}
void TransferEnginePrivate::delayedExitSafely()
{
Sep 30, 2014
Sep 30, 2014
195
196
qDebug() << "Stopping transfer engine";
qApp->exit();
197
198
199
200
201
202
203
204
205
}
void TransferEnginePrivate::enabledPluginsCheck()
{
Q_Q(TransferEngine);
if (m_fileWatcherTimer->isActive()) {
m_fileWatcherTimer->stop();
}
Nov 22, 2013
Nov 22, 2013
206
207
208
209
210
if (m_infoObjects.count() > 0) {
qWarning() << Q_FUNC_INFO << "Already quering account info" << m_infoObjects.count();
return;
}
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// First clear old data
m_enabledPlugins.clear();
qDeleteAll(m_infoObjects);
m_infoObjects.clear();
QPluginLoader loader;
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);
// Handle the case if all the plugins have been removed.
QStringList plugins = pluginList();
if (plugins.isEmpty()) {
emit q->transferMethodListChanged();
return;
}
// We have plugins
Q_FOREACH(QString plugin, plugins) {
loader.setFileName(plugin);
TransferPluginInterface *interface =
qobject_cast<TransferPluginInterface*>(loader.instance());
if (interface && interface->enabled()) {
TransferPluginInfo *info = interface->infoObject();
if (!info) {
qWarning() << Q_FUNC_INFO << "NULL Info object!";
continue;
}
Nov 22, 2013
Nov 22, 2013
240
241
242
243
if (info->ready()) {
if (info->info().count() > 0) {
m_enabledPlugins << info->info();
}
Sep 22, 2014
Sep 22, 2014
244
delete info;
Nov 22, 2013
Nov 22, 2013
245
246
247
248
249
250
251
} else {
// These object will be cleaned in pluginInfoReady() slot.
m_infoObjects << info;
connect(info, SIGNAL(infoReady()), this, SLOT(pluginInfoReady()));
connect(info, SIGNAL(infoError(QString)), this, SLOT(pluginInfoError(QString)));
info->query();
}
252
253
254
255
256
257
}
if (!interface) {
qWarning() << Q_FUNC_INFO << loader.errorString();
}
}
Jul 1, 2013
Jul 1, 2013
258
}
Jul 1, 2013
Jul 1, 2013
260
261
262
263
264
265
266
267
268
269
270
271
void TransferEnginePrivate::cleanupExpiredTransfers(const QList<int> &expiredIds)
{
// Clean up expired items from the database by changing the status to TransferInterrupted. This way
// database doesn't maintain objects with unifinished state and failed items can be cleaned by the
// user manually from the UI.
Q_Q(TransferEngine);
Q_FOREACH(int id, expiredIds) {
if (DbManager::instance()->updateTransferStatus(id, TransferEngineData::TransferInterrupted)) {
m_activityMonitor->activityFinished(id);
emit q->statusChanged(id, TransferEngineData::TransferInterrupted);
}
}
272
273
274
275
276
277
278
}
void TransferEnginePrivate::recoveryCheck()
{
QList<TransferDBRecord> records = DbManager::instance()->transfers();
// Check all transfer which are not properly finished and mark those as
// interrupted
Jul 1, 2013
Jul 1, 2013
279
Q_Q(TransferEngine);
280
281
282
Q_FOREACH(TransferDBRecord record, records) {
if (record.status == TransferEngineData::TransferStarted ||
record.status == TransferEngineData::NotStarted) {
Jul 1, 2013
Jul 1, 2013
283
284
285
if (DbManager::instance()->updateTransferStatus(record.transfer_id, TransferEngineData::TransferInterrupted)) {
emit q->statusChanged(record.transfer_id, TransferEngineData::TransferInterrupted);
}
286
287
288
289
290
291
}
}
}
void TransferEnginePrivate::sendNotification(TransferEngineData::TransferType type,
TransferEngineData::TransferStatus status,
Oct 2, 2013
Oct 2, 2013
292
const QString &fileName)
Oct 2, 2013
Oct 2, 2013
294
if (!m_notificationsEnabled || fileName.isEmpty()) {
295
296
297
298
299
300
301
302
return;
}
QString msgGSummary;
QString msgNBody;
QString eventType;
bool bannerOnly = false;
Apr 18, 2013
Apr 18, 2013
303
304
305
306
307
308
309
310
QList<MNotification*> nList = MNotification::notifications();
QMultiMap <QString, MNotification*> nMap;
// Get the existing notifications and sort them based on their event types
foreach(MNotification *n, nList) {
nMap.insert(n->eventType(), n);
}
311
312
313
314
315
316
317
318
319
320
321
if (status == TransferEngineData::TransferFinished) {
switch(type) {
case TransferEngineData::Upload:
//: Notification for successful file upload
//% "File uploaded"
msgNBody = qtTrId("transferengine-no-file-upload-success");
msgGSummary.clear();
eventType = MNotification::TransferEvent; // Use "generic" transfer event for uploads
bannerOnly = true;
break;
case TransferEngineData::Download:
Apr 18, 2013
Apr 18, 2013
322
eventType = MNotification::TransferCompleteEvent;
323
324
325
326
//: Notification for successful file download
//% "File downloaded"
msgNBody = qtTrId("transferengine-no-file-download-success");
//: NotificationGroup summary for successful download
Apr 18, 2013
Apr 18, 2013
327
328
329
//% "%n file(s) downloaded"
msgGSummary = qtTrId("transferengine-no-number-of-downloads",
nMap.values(eventType).count() + 1);
330
331
332
333
334
335
336
337
338
339
break;
case TransferEngineData::Sync:
// Ok exit
return;
default:
qWarning() << "TransferEnginePrivate::sendNotification: unknown state";
return;
}
} else {
if (status == TransferEngineData::TransferInterrupted) {
Apr 18, 2013
Apr 18, 2013
340
341
eventType = MNotification::TransferErrorEvent;
342
343
344
345
346
switch (type) {
case TransferEngineData::Upload:
//: Notification for failed file upload
//% "Upload failed!"
msgNBody = qtTrId("transferengine-no-file-upload-failure");
Apr 18, 2013
Apr 18, 2013
347
348
349
//% "%n upload(s) failed"
msgGSummary = qtTrId("transferengine-no-number-of-upload-failures",
nMap.values(eventType).count() + 1);
350
351
break;
case TransferEngineData::Download:
Apr 18, 2013
Apr 18, 2013
352
//: Notification for failed file download
353
354
//% "Download failed!"
msgNBody = qtTrId("transferengine-no-file-download-failure");
Apr 18, 2013
Apr 18, 2013
355
356
357
//% "%n download(s) failed"
msgGSummary = qtTrId("transferengine-no-number-of-download-failures",
nMap.values(eventType).count() + 1);
358
359
360
361
362
break;
case TransferEngineData::Sync:
//: Notification for sync failure
//% "Sync failed!"
msgNBody = qtTrId("transferengine-no-sync-failure");
Apr 18, 2013
Apr 18, 2013
363
364
365
//% "%n sync(s) failed"
msgGSummary = qtTrId("transferengine-no-number-of-sync-failures",
nMap.values(eventType).count() + 1);
366
367
368
369
370
371
break;
default:
qWarning() << "TransferEnginePrivate::sendNotification: unknown state";
return;
}
Apr 18, 2013
Apr 18, 2013
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
} else {
if (status == TransferEngineData::TransferCanceled) {
// Exit, no banners or events when user has canceled a transfer
return;
}}}
// Notification & Banner rules:
//
// Show Banner:
// - For succesfull uploads and for downloads
// - For failed Uploads, Downloads, Syncs
//
// Show an event in the EventView:
// - For downloads
// - For failed uploads, downloads and syncs
//
// Use grouping always
if (!(msgNBody.isEmpty() && eventType.isEmpty())) {
// First create the notification
MNotification notification(eventType);
notification.setSummary(fileName);
notification.setBody(msgNBody);
notification.setImage("icon-l-share");
// Check if we have existing group and use that instead of creating a new one.
QList<MNotificationGroup*> groups = MNotificationGroup::notificationGroups();
MNotificationGroup *group = 0;
if (groups.count() > 0) {
foreach(MNotificationGroup *g, groups) {
if (g->eventType() == eventType) {
group = g;
break;
}
}
}
// No existing groups, create a new one from the scratch
if (group == 0){
group = new MNotificationGroup(eventType);
group->setImage("icon-l-share");
// Add to the groups, it will be deleted when the list is cleaned
groups.append(group);
}
if (bannerOnly) {
// This makes notifications to appear banners only
group->setSummary(QString());
} else {
// This is the summary text which is shown when notifications are grouped
Apr 18, 2013
Apr 18, 2013
423
group->setSummary(msgGSummary);
424
425
426
427
428
429
430
431
432
433
434
435
group->setBody(fileName);
}
// Set default action for groups
MRemoteAction rAct = createRemoteActionForGroup();
if (!rAct.toString().isEmpty()) {
group->setAction(rAct);
}
notification.setGroup(*group);
notification.publish();
Oct 2, 2013
Oct 2, 2013
436
437
438
// always publish the group to make updates appear
group->publish();
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
// Cleanup
if (groups.count()) {
qDeleteAll(groups);
}
if (nList.count()) {
qDeleteAll(nList);
}
}
}
int TransferEnginePrivate::uploadMediaItem(MediaItem *mediaItem,
MediaTransferInterface *muif,
const QVariantMap &userData)
{
Q_Q(TransferEngine);
if (muif == 0) {
qWarning() << "TransferEngine::uploadMediaItem invalid MediaItem";
return -1;
}
if (muif == 0) {
qWarning() << "TransferEngine::uploadMediaItem Failed to get MediaTransferInterface";
return -1;
}
mediaItem->setValue(MediaItem::TransferType, TransferEngineData::Upload);
mediaItem->setValue(MediaItem::DisplayName, muif->displayName());
mediaItem->setValue(MediaItem::ServiceIcon, muif->serviceIcon());
mediaItem->setValue(MediaItem::CancelSupported, muif->cancelEnabled());
mediaItem->setValue(MediaItem::RestartSupported, muif->restartEnabled());
// Get and set data from user data if that's set. The following user data values
// are stored to the database so that's why they are set to the media item too.
// If the user data is fully custom for plugin it won't be stored to the database and
// it's up to the plugin to handle or ignore it.
QString title = userData.value("title").toString();
QString desc = userData.value("description").toString();
qint64 accId = userData.value("accountId").toInt();
qreal scale = userData.value("scalePercent").toReal();
mediaItem->setValue(MediaItem::Title, title);
mediaItem->setValue(MediaItem::Description, desc);
mediaItem->setValue(MediaItem::AccountId, accId);
mediaItem->setValue(MediaItem::ScalePercent, scale);
muif->setMediaItem(mediaItem);
connect(muif, SIGNAL(statusChanged(MediaTransferInterface::TransferStatus)),
this, SLOT(uploadItemStatusChanged(MediaTransferInterface::TransferStatus)));
connect(muif, SIGNAL(progressUpdated(qreal)),
this, SLOT(updateProgress(qreal)));
// Let's create an entry into Transfer DB
const int key = DbManager::instance()->createTransferEntry(mediaItem);
m_keyTypeCache.insert(key, TransferEngineData::Upload);
if (key < 0) {
qWarning() << "TransferEngine::uploadMediaItem: Failed to create an entry to transfer database!";
delete muif;
return key;
}
Jul 1, 2013
Jul 1, 2013
501
m_activityMonitor->newActivity(key);
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
emit q->transfersChanged();
emit q->statusChanged(key, TransferEngineData::NotStarted);
// For now, we just store our uploader to a map. It'll be removed from it when
// the upload has finished.
m_plugins.insert(muif, key);
muif->start();
return key;
}
QStringList TransferEnginePrivate::pluginList() const
{
QDir dir(SHARE_PLUGINS_PATH);
QStringList plugins = dir.entryList(QStringList() << "*.so",
QDir::Files,
QDir::NoSort);
QStringList filePaths;
Q_FOREACH(QString plugin, plugins) {
filePaths << dir.absolutePath() + QDir::separator() + plugin;
}
return filePaths;
}
QList <TransferMethodInfo> TransferEnginePrivate::enabledPlugins() const
{
return m_enabledPlugins;
}
MediaTransferInterface *TransferEnginePrivate::loadPlugin(const QString &pluginId) const
{
QPluginLoader loader;
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);
Q_FOREACH(QString plugin, pluginList()) {
loader.setFileName(plugin);
TransferPluginInterface *interface =
qobject_cast<TransferPluginInterface*>(loader.instance());
if (interface && interface->enabled() && interface->pluginId() == pluginId) {
return interface->transferObject();
}
if (!interface) {
qWarning() << "TransferEngine::loadPlugin: " + loader.errorString();
}
if (loader.isLoaded()) {
loader.unload();
}
}
return 0;
}
Oct 2, 2013
Oct 2, 2013
558
559
560
561
562
563
564
565
566
567
568
569
570
QString TransferEnginePrivate::mediaFileOrResourceName(MediaItem *mediaItem) const
{
if (!mediaItem) {
return QString();
}
QUrl url = mediaItem->value(MediaItem::Url).toUrl();
if (!url.isEmpty()) {
QStringList split = url.toString().split(QDir::separator());
return split.at(split.length()-1);
}
return mediaItem->value(MediaItem::ResourceName).toString();
}
571
572
573
574
575
576
577
578
579
580
581
582
583
void TransferEnginePrivate::uploadItemStatusChanged(MediaTransferInterface::TransferStatus status)
{
MediaTransferInterface *muif = qobject_cast<MediaTransferInterface*>(sender());
const int key = m_plugins.value(muif);
const TransferEngineData::TransferType type =
static_cast<TransferEngineData::TransferType>(muif->mediaItem()->value(MediaItem::TransferType).toInt());
TransferEngineData::TransferStatus tStatus = static_cast<TransferEngineData::TransferStatus>(status);
bool ok = false;
switch(tStatus) {
case TransferEngineData::TransferStarted:
ok = DbManager::instance()->updateTransferStatus(key, tStatus);
Jul 1, 2013
Jul 1, 2013
584
m_activityMonitor->newActivity(key);
585
586
587
588
589
590
591
592
593
break;
case TransferEngineData::TransferInterrupted:
case TransferEngineData::TransferCanceled:
case TransferEngineData::TransferFinished:
{
// If the flow ends up here, we are not interested in any signals the same object
// might emit. Let's just disconnect them.
muif->disconnect();
Oct 2, 2013
Oct 2, 2013
594
sendNotification(type, tStatus, mediaFileOrResourceName(muif->mediaItem()));
595
596
597
598
599
600
601
ok = DbManager::instance()->updateTransferStatus(key, tStatus);
if (m_plugins.remove(muif) == 0) {
qWarning() << "TransferEnginePrivate::uploadItemStatusChanged: Failed to remove media upload object from the map!";
// What to do here.. Let's just delete it..
}
muif->deleteLater();
muif = 0;
Jul 1, 2013
Jul 1, 2013
602
m_activityMonitor->activityFinished(key);
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
} break;
default:
qWarning() << "TransferEnginePrivate::uploadItemStatusChanged: unhandled status: " << tStatus;
break;
}
if (!ok) {
qWarning() << "TransferEnginePrivate::uploadItemStatusChanged: Failed update share status for the item: " + key;
return;
}
Q_Q(TransferEngine);
emit q->statusChanged(key, tStatus);
}
void TransferEnginePrivate::updateProgress(qreal progress)
{
MediaTransferInterface *muif = qobject_cast<MediaTransferInterface*>(sender());
const int key = m_plugins.value(muif);
if (!DbManager::instance()->updateProgress(key, progress)) {
qWarning() << "TransferEnginePrivate::updateProgress: Failed to update progress";
return;
}
Jul 1, 2013
Jul 1, 2013
629
m_activityMonitor->newActivity(key);
630
631
632
633
634
635
636
637
Q_Q(TransferEngine);
emit q->progressChanged(key, progress);
}
void TransferEnginePrivate::pluginInfoReady()
{
TransferPluginInfo *infoObj = qobject_cast<TransferPluginInfo*>(sender());
Nov 22, 2013
Nov 22, 2013
638
639
640
641
642
643
644
645
646
647
648
QList<TransferMethodInfo> infoList = infoObj->info();
if (!infoList.isEmpty()) {
m_enabledPlugins << infoList;
}
if (m_infoObjects.removeOne(infoObj)) {
delete infoObj;
} else {
qWarning() << Q_FUNC_INFO << "Failed to remove info object!";
delete infoObj;
}
649
650
651
652
653
654
655
656
657
658
659
660
if (m_infoObjects.isEmpty()) {
Q_Q(TransferEngine);
emit q->transferMethodListChanged();
}
}
void TransferEnginePrivate::pluginInfoError(const QString &msg)
{
qWarning() << "TransferEnginePrivate::pluginInfoError:" << msg;
TransferPluginInfo *infoObj = qobject_cast<TransferPluginInfo*>(sender());
m_infoObjects.removeOne(infoObj);
May 5, 2014
May 5, 2014
661
infoObj->deleteLater();
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
if (m_infoObjects.isEmpty()) {
Q_Q(TransferEngine);
emit q->transferMethodListChanged();
}
}
TransferEngineData::TransferType TransferEnginePrivate::transferType(int transferId)
{
if (!m_keyTypeCache.contains(transferId)) {
TransferEngineData::TransferType type = DbManager::instance()->transferType(transferId);
m_keyTypeCache.insert(transferId, type);
return type;
} else {
return m_keyTypeCache.value(transferId);
}
}
void TransferEnginePrivate::callbackCall(int transferId, CallbackMethodType method)
{
// Get DBus callback information. Callback list must contain at least
// service, path, interface and one callback method name. Note that even
// if the cancel or restart method is missing, it'll be indicated as an
// empty string. So the list length is always 5.
QStringList callback = DbManager::instance()->callback(transferId);
if (callback.length() != 5) {
qWarning() << "TransferEnginePrivate:callbackCall: Invalid callback interface";
return;
}
QDBusInterface remoteInterface(callback.at(Service),
callback.at(Path),
callback.at(Interface));
if (!remoteInterface.isValid()) {
qWarning() << "TransferEnginePrivate::callbackCall: DBus interface is not valid!";
return;
}
if (method >= callback.size()) {
qWarning() << "TransferEnginePrivate::callbackCall: method index out of range!";
return;
}
const QString methodName = callback.at(method);
if (methodName.isEmpty()) {
qWarning() << "TransferEnginePrivate::callbackCall: Failed to get callback method name!";
return;
Jul 1, 2013
Jul 1, 2013
712
713
}
remoteInterface.call(methodName, transferId);
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
}
MRemoteAction TransferEnginePrivate::createRemoteActionForGroup()
{
if (m_settings.status() != QSettings::NoError) {
qWarning() << Q_FUNC_INFO << "Failed to read settings!" << m_settings.status();
return MRemoteAction();
}
m_settings.beginGroup("transfers");
const QString service = m_settings.value("service").toString();
const QString path = m_settings.value("path").toString();
const QString iface = m_settings.value("interface").toString();
const QString method = m_settings.value("method").toString();
m_settings.endGroup();
return MRemoteAction(service, path, iface, method);
}
/*!
\class TransferEngine
\brief The TransferEngine class implements the functionality for different transfer types.
\ingroup transfer-engine
TransferEngine is the central place for:
\list
\o Sharing - Provides requires plugin interfaces for share plugins
\o Downloads - Provides an API to create Download entries
\o Syncs - Provides an API to create Sync entries
\endlist
For Downloads and Syncs, the Transfer Engine acts only a place to keep track of these operations.
The actual Download and Sync is executed by a client using TransferEngine API. For sharing
the TransferEngine provides an API containing a few interaces, which a share plugin must implement.
TransferEngine also takes care of loading and executing the sharing, based on the API it defines.
The most essential thing to remember is that Transfer Engine provides share plugin API, DBus API e.g.
for creating Transfer UI or Share UIs, it stores data to the local sqlite database using DbManager
and that's it.
How to implement a share plugin see: TransferPluginInterface, MediaTransferInterface, MediaItem,
TransferPluginInfo
TransferEngine provides DBus API, but instead of using it directly, it's recommended to use
TransferEngineClient. If there is a need to create UI to display e.g. transfer statuses, then
the DBus API is the recommend way to implement it.
*/
/*!
\fn void TransferEngine::progressChanged(int transferId, double progress)
The signal is emitted when \a progress for a transfer with a \a transferId has changed.
*/
/*!
\fn void TransferEngine::statusChanged(int transferId, int status)
The signal is emitted when \a status for a transfer with a \a transferId has changed.
*/
/*!
\fn void TransferEngine::transferMethodListChanged()
The signal is emitted when transfer methods have changed. Usually tranfer methods change
when a new plugin is installed to the system or an account has been enabled or disabled.
*/
/*!
\fn void TransferEngine::transfersChanged()
The signal is emitted when there is a new transfer or a transfer has been removed from the
database.
*/
/*!
Constructor with optional \a parent arguement.
*/
TransferEngine::TransferEngine(QObject *parent) :
QObject(parent),
d_ptr(new TransferEnginePrivate(this))
{
TransferMethodInfo::registerType();
TransferDBRecord::registerType();
QDBusConnection connection = QDBusConnection::sessionBus();
if (!connection.registerService("org.nemo.transferengine")) {
qFatal("DBUS service already taken. Kill the other instance first.");
}
if (!connection.registerObject("/org/nemo/transferengine", this)) {
qFatal("Could not register object \'/org/nemo/transferengine\'");
}
Jul 1, 2013
Jul 1, 2013
810
new TransferEngineAdaptor(this);
811
812
813
814
815
816
// Let's make sure that db is open by creating
// DbManager singleton instance.
DbManager::instance();
Q_D(TransferEngine);
d->recoveryCheck();
Jul 1, 2013
Jul 1, 2013
817
d->enabledPluginsCheck();
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
}
/*!
Destroys the TransferEngine object.
*/
TransferEngine::~TransferEngine()
{
Q_D(TransferEngine);
d->recoveryCheck();
delete d_ptr;
d_ptr = 0;
QDBusConnection connection = QDBusConnection::sessionBus();
connection.unregisterObject("/org/nemo/transferengine");
if (!connection.unregisterService("org.nemo.transferengine")) {
qWarning() << "Failed to unregister org.nemo.tranferengine service";
}
}
/*!
DBus adaptor calls this method to start uploading a media item. The minimum information
needed to start an upload and to create an entry to the transfer database is:
\a source the path to the media item to be downloaded. \a serviceId the ID of the share
plugin. See TransferPluginInterface::pluginId() for more details. \a mimeType is the MimeType
of the media item e.g. "image/jpeg". \a metadataStripped boolean to indicate if the metadata
should be kept or removed before uploading. \a userData is various kind of data which share UI
may provide to the engine. UserData is QVariant map i.e. the data must be provided as key-value
pairs, where the keys must be QStrings.
TransferEngine handles the following user defined data automatically and stores them to the database:
\list
\o "title" Title for the media
\o "description" Description for the media
\o "accountId" The ID of the account which is used for sharing. See qt-accounts for more details.
\o "scalePercent" The scale percent e.g. downscale image to 50% from original before uploading.
\endlist
In practice this method instantiates a share plugin with \a serviceId and passes a MediaItem instance filled
with required data to it. When the plugin has been loaded, the MediaTransferInterface::start() method is called
and the actual sharing starts.
This method returns a transfer ID which can be used later to fetch information of this specific transfer.
*/
int TransferEngine::uploadMediaItem(const QString &source,
const QString &serviceId,
const QString &mimeType,
bool metadataStripped,
const QVariantMap &userData)
{
Q_D(TransferEngine);
Oct 1, 2014
Oct 1, 2014
870
871
d->exitSafely();
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
MediaTransferInterface *muif = d->loadPlugin(serviceId);
if (muif == 0) {
qWarning() << "TransferEngine::uploadMediaItem Failed to get MediaTransferInterface";
return -1;
}
QUrl filePath(source);
QFileInfo fileInfo(filePath.toLocalFile());
if (!fileInfo.exists()) {
qWarning() << "TransferEnginePrivate::uploadMediaItem file " << source << " doesn't exist!";
}
MediaItem *mediaItem = new MediaItem(muif);
mediaItem->setValue(MediaItem::Url, filePath);
mediaItem->setValue(MediaItem::MetadataStripped, metadataStripped);
mediaItem->setValue(MediaItem::ResourceName, fileInfo.fileName());
mediaItem->setValue(MediaItem::MimeType, mimeType);
mediaItem->setValue(MediaItem::FileSize, fileInfo.size());
mediaItem->setValue(MediaItem::PluginId, serviceId);
mediaItem->setValue(MediaItem::UserData, userData);
return d->uploadMediaItem(mediaItem, muif, userData);
}
/*!
DBus adaptor calls this method to start uploading media item content. Sometimes the content
to be shared is not a file, but data e.g. contact information in vcard format. In order to
avoid serializing data to a file, pass url to the file, reading the data, deleting the file,
TransferEngine provides this convenience API.
\a content is the media item content to be shared. \a serviceId is the id of the share plugin. See
TransferPluginInterface::pluginId() for more details. \a userData is a QVariantMap containing
share plugin specific data. See TransferEngine::uploadMediaItem for more details.
This method returns a transfer ID which can be used later to fetch information of this specific transfer.
*/
int TransferEngine::uploadMediaItemContent(const QVariantMap &content,
const QString &serviceId,
const QVariantMap &userData)
{
Q_D(TransferEngine);
Oct 1, 2014
Oct 1, 2014
912
913
d->exitSafely();
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
MediaTransferInterface *muif = d->loadPlugin(serviceId);
if (muif == 0) {
qWarning() << "TransferEngine::uploadMediaItemContent Failed to get MediaTransferInterface";
return -1;
}
MediaItem *mediaItem = new MediaItem(muif);
mediaItem->setValue(MediaItem::ContentData, content.value("data"));
mediaItem->setValue(MediaItem::ResourceName, content.value("name"));
mediaItem->setValue(MediaItem::MimeType, content.value("type"));
mediaItem->setValue(MediaItem::ThumbnailIcon, content.value("icon"));
mediaItem->setValue(MediaItem::PluginId, serviceId);
mediaItem->setValue(MediaItem::UserData, userData);
return d->uploadMediaItem(mediaItem, muif, userData);
}
/*!
DBus adaptor calls this method to create a download entry. Note that this is purely write-only
method and doesn't involve anything else from TransferEngine side than creating a new DB record
of type 'Download'.
\list
\o \a displayName The name for Download which may be used by the UI displaying the download
\o \a applicationIcon The application icon of the application created the download
\o \a serviceIcon The service icon, which provides the file to be downloaded
\o \a filePath The filePath e.g. url to the file to be downloaded
\o \a mimeType the MimeType of the file to be downloaded
\o \a expectedFileSize The file size of the file to be downloaded
\o \a callback QStringList containing DBus callback information such as: service, path and interface
\o \a cancelMethod The name of the cancel callback method, which DBus callback provides
\o \a restartMethod The name of the restart callback method, which DBus callback provides
\endlist
This method returns the transfer id of the created Download transfer. Note that this method only
creates an entry to the database. To start the actual transfer, the startTransfer() method must
be called.
\sa startTransfer(), restartTransfer(), finishTransfer(), updateTransferProgress()
*/
int TransferEngine::createDownload(const QString &displayName,
const QString &applicationIcon,
const QString &serviceIcon,
const QString &filePath,
const QString &mimeType,
qlonglong expectedFileSize,
const QStringList &callback,
const QString &cancelMethod,
const QString &restartMethod)
{
Q_D(TransferEngine);
QUrl url(filePath);
QFileInfo fileInfo(filePath);
MediaItem *mediaItem = new MediaItem();
mediaItem->setValue(MediaItem::Url, url);
mediaItem->setValue(MediaItem::ResourceName, fileInfo.fileName());
mediaItem->setValue(MediaItem::MimeType, mimeType);
mediaItem->setValue(MediaItem::TransferType, TransferEngineData::Download);
mediaItem->setValue(MediaItem::FileSize, expectedFileSize);
mediaItem->setValue(MediaItem::DisplayName, displayName);
mediaItem->setValue(MediaItem::ApplicationIcon, applicationIcon);
mediaItem->setValue(MediaItem::ServiceIcon, serviceIcon);
mediaItem->setValue(MediaItem::Callback, callback);
mediaItem->setValue(MediaItem::CancelCBMethod, cancelMethod);
mediaItem->setValue(MediaItem::RestartCBMethod, restartMethod);
mediaItem->setValue(MediaItem::CancelSupported, !cancelMethod.isEmpty());
mediaItem->setValue(MediaItem::RestartSupported,!restartMethod.isEmpty());
const int key = DbManager::instance()->createTransferEntry(mediaItem);
Jul 1, 2013
Jul 1, 2013
983
d->m_activityMonitor->newActivity(key);
984
985
d->m_keyTypeCache.insert(key, TransferEngineData::Download);
emit transfersChanged();
Jul 1, 2013
Jul 1, 2013
986
emit statusChanged(key, TransferEngineData::NotStarted);
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
return key;
}
/*!
DBus adaptor calls this method to create a Sync entry. Note that this is purely write-only
method and doesn't involve anything else from TransferEngine side than creating a new DB record
of type 'Download'.
\list
\o \a displayName The name for download which may be used by the UI displaying the download
\o \a applicationIcon The application icon of the application created the download
\o \a serviceIcon The service icon, which provides the file to be downloaded
\o \a callback QStringList containing DBus callback information such as: service, path and interface
\o \a cancelMethod The name of the cancel callback method, which DBus callback provides