1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2013 Utku Aydın <utkuaydin34@gmail.com>
4 //
5 
6 #include "RouteSyncManager.h"
7 
8 #include "GeoDataParser.h"
9 #include "MarbleDirs.h"
10 #include "MarbleDebug.h"
11 #include "GeoDataFolder.h"
12 #include "GeoDataDocument.h"
13 #include "GeoDataPlacemark.h"
14 #include "CloudRouteModel.h"
15 #include "CloudRoutesDialog.h"
16 #include "CloudSyncManager.h"
17 #include "OwncloudSyncBackend.h"
18 #include "RouteItem.h"
19 #include "RoutingManager.h"
20 
21 #include <QDir>
22 #include <QUrl>
23 #include <QFile>
24 #include <QIcon>
25 #include <QPointer>
26 
27 namespace Marble
28 {
29 
30 /**
31  * Private class for RouteSyncManager.
32  */
33 class Q_DECL_HIDDEN RouteSyncManager::Private {
34 public:
35     Private( CloudSyncManager *cloudSyncManager );
36     ~Private();
37 
38     bool m_routeSyncEnabled;
39     CloudSyncManager *m_cloudSyncManager;
40     RoutingManager *m_routingManager;
41     CloudRouteModel *m_model;
42 
43     QDir m_cacheDir;
44     OwncloudSyncBackend m_owncloudBackend;
45     QVector<RouteItem> m_routeList;
46 };
47 
Private(CloudSyncManager * cloudSyncManager)48 RouteSyncManager::Private::Private( CloudSyncManager *cloudSyncManager ) :
49     m_routeSyncEnabled( false ),
50     m_cloudSyncManager( cloudSyncManager ),
51     m_routingManager( nullptr ),
52     m_model( new CloudRouteModel() ),
53     m_owncloudBackend( cloudSyncManager )
54 {
55     m_cacheDir = QDir(MarbleDirs::localPath() + QLatin1String("/cloudsync/cache/routes/"));
56 }
57 
~Private()58 RouteSyncManager::Private::~Private()
59 {
60     delete m_model;
61 }
62 
RouteSyncManager(CloudSyncManager * cloudSyncManager)63 RouteSyncManager::RouteSyncManager(CloudSyncManager *cloudSyncManager) :
64     d( new Private( cloudSyncManager ) )
65 {
66     connect( &d->m_owncloudBackend, SIGNAL(routeUploadProgress(qint64,qint64)), this, SLOT(updateUploadProgressbar(qint64,qint64)) );
67     connect( &d->m_owncloudBackend, SIGNAL(routeListDownloaded(QVector<RouteItem>)), this, SLOT(setRouteModelItems(QVector<RouteItem>)) );
68     connect( &d->m_owncloudBackend, SIGNAL(routeListDownloadProgress(qint64,qint64)), this, SIGNAL(routeListDownloadProgress(qint64,qint64)) );
69     connect( &d->m_owncloudBackend, SIGNAL(routeDownloadProgress(qint64,qint64)), d->m_model, SLOT(updateProgress(qint64,qint64)) );
70     connect( &d->m_owncloudBackend, SIGNAL(routeDownloaded()), this, SLOT(prepareRouteList()) );
71     connect( &d->m_owncloudBackend, SIGNAL(routeDeleted()), this, SLOT(prepareRouteList()) );
72     connect( &d->m_owncloudBackend, SIGNAL(removedFromCache(QString)), this, SLOT(prepareRouteList()) );
73 }
74 
~RouteSyncManager()75 RouteSyncManager::~RouteSyncManager()
76 {
77     delete d;
78 }
79 
setRoutingManager(RoutingManager * routingManager)80 void RouteSyncManager::setRoutingManager(RoutingManager *routingManager)
81 {
82     d->m_routingManager = routingManager;
83 }
84 
isRouteSyncEnabled() const85 bool RouteSyncManager::isRouteSyncEnabled() const
86 {
87     return d->m_routeSyncEnabled && d->m_cloudSyncManager && d->m_cloudSyncManager->isSyncEnabled();
88 }
89 
setRouteSyncEnabled(bool enabled)90 void RouteSyncManager::setRouteSyncEnabled( bool enabled )
91 {
92     if ( d->m_routeSyncEnabled != enabled ) {
93         d->m_routeSyncEnabled = enabled;
94         emit routeSyncEnabledChanged( d->m_routeSyncEnabled );
95     }
96 }
97 
model()98 CloudRouteModel* RouteSyncManager::model()
99 {
100     return d->m_model;
101 }
102 
generateTimestamp() const103 QString RouteSyncManager::generateTimestamp() const
104 {
105     qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
106     return QString::number( timestamp );
107 }
108 
saveDisplayedToCache() const109 QString RouteSyncManager::saveDisplayedToCache() const
110 {
111     if ( !d->m_routingManager ) {
112         qWarning() << "RoutingManager instance not set in RouteSyncManager. Cannot save current route.";
113         return QString();
114     }
115 
116     d->m_cacheDir.mkpath( d->m_cacheDir.absolutePath() );
117 
118     const QString timestamp = generateTimestamp();
119     const QString filename = d->m_cacheDir.absolutePath() + QLatin1Char('/') + timestamp + QLatin1String(".kml");
120     d->m_routingManager->saveRoute( filename );
121     return timestamp;
122 }
123 
uploadRoute()124 void RouteSyncManager::uploadRoute()
125 {
126     if( !d->m_cloudSyncManager->workOffline() ) {
127         d->m_owncloudBackend.uploadRoute( saveDisplayedToCache() );
128     }
129 }
130 
cachedRouteList() const131 QVector<RouteItem> RouteSyncManager::cachedRouteList() const
132 {
133     QVector<RouteItem> routeList;
134     QStringList cachedRoutes = d->m_cacheDir.entryList( QStringList() << "*.kml", QDir::Files );
135     for ( const QString &routeFilename: cachedRoutes ) {
136         QFile file(d->m_cacheDir.absolutePath() + QLatin1Char('/') + routeFilename);
137         file.open( QFile::ReadOnly );
138 
139         GeoDataParser parser( GeoData_KML );
140         if( !parser.read( &file ) ) {
141             mDebug() << QLatin1String("Could not read ") + routeFilename;
142         }
143 
144         file.close();
145 
146         QString routeName;
147         GeoDocument *geoDoc = parser.releaseDocument();
148         GeoDataDocument *container = dynamic_cast<GeoDataDocument*>( geoDoc );
149         if ( container && container->size() > 0 ) {
150             GeoDataFolder *folder = container->folderList().at( 0 );
151             for ( GeoDataPlacemark *placemark: folder->placemarkList() ) {
152                 routeName += placemark->name() + QLatin1String(" - ");
153             }
154         }
155 
156         routeName = routeName.left( routeName.length() - 3 );
157         QString timestamp = routeFilename.left( routeFilename.length() - 4 );
158         QString distance(QLatin1Char('0'));
159         QString duration(QLatin1Char('0'));
160 
161         QString previewPath = QString( "%0/preview/%1.jpg" ).arg( d->m_cacheDir.absolutePath(), timestamp );
162         QIcon preview;
163 
164         if( QFile( previewPath ).exists() ) {
165             preview = QIcon( previewPath );
166         }
167 
168         // Would that work on Windows?
169         QUrl previewUrl( QString( "file://%0" ).arg( previewPath ) );
170 
171         RouteItem item;
172         item.setIdentifier( timestamp );
173         item.setName( routeName );
174         item.setDistance( distance );
175         item.setDistance( duration );
176         item.setPreview( preview );
177         item.setPreviewUrl( previewUrl );
178         item.setOnCloud( false );
179         routeList.append( item );
180     }
181 
182     return routeList;
183 }
184 
uploadRoute(const QString & timestamp)185 void RouteSyncManager::uploadRoute( const QString &timestamp )
186 {
187     if( !d->m_cloudSyncManager->workOffline() ) {
188         d->m_owncloudBackend.uploadRoute( timestamp );
189     }
190 }
191 
prepareRouteList()192 void RouteSyncManager::prepareRouteList()
193 {
194     d->m_routeList.clear();
195 
196     QVector<RouteItem> cachedRoutes = cachedRouteList();
197     for( const RouteItem &item: cachedRoutes ) {
198         d->m_routeList.append( item );
199     }
200 
201     if( !d->m_cloudSyncManager->workOffline() ) {
202         d->m_owncloudBackend.downloadRouteList();
203     } else {
204         // If not offline, setRouteModelItems() does this after
205         // appending downloaded items to the list.
206         d->m_model->setItems( d->m_routeList );
207     }
208 }
209 
downloadRoute(const QString & timestamp)210 void RouteSyncManager::downloadRoute( const QString &timestamp )
211 {
212     d->m_owncloudBackend.downloadRoute( timestamp );
213 }
214 
openRoute(const QString & timestamp)215 void RouteSyncManager::openRoute(const QString &timestamp )
216 {
217     if ( !d->m_routingManager ) {
218         qWarning() << "RoutingManager instance not set in RouteSyncManager. Cannot open route " << timestamp;
219         return;
220     }
221 
222     d->m_routingManager->loadRoute( QString( "%0/%1.kml" )
223                                     .arg( d->m_cacheDir.absolutePath() )
224                                     .arg( timestamp ) );
225 }
226 
deleteRoute(const QString & timestamp)227 void RouteSyncManager::deleteRoute(const QString &timestamp )
228 {
229     d->m_owncloudBackend.deleteRoute( timestamp );
230 }
231 
removeRouteFromCache(const QString & timestamp)232 void RouteSyncManager::removeRouteFromCache( const QString &timestamp )
233 {
234     d->m_owncloudBackend.removeFromCache( d->m_cacheDir, timestamp );
235 }
236 
updateUploadProgressbar(qint64 sent,qint64 total)237 void RouteSyncManager::updateUploadProgressbar( qint64 sent, qint64 total )
238 {
239     emit routeUploadProgress( sent, total );
240     if( sent == total ) {
241         prepareRouteList();
242     }
243 }
244 
setRouteModelItems(const QVector<RouteItem> & routeList)245 void RouteSyncManager::setRouteModelItems( const QVector<RouteItem> &routeList )
246 {
247     if( d->m_routeList.count() > 0 ) {
248         QStringList cloudRoutes;
249         for( const RouteItem &item: routeList ) {
250             cloudRoutes.append( item.identifier() );
251         }
252 
253         for( int position = 0; position < d->m_routeList.count(); position++ ) {
254             if( cloudRoutes.contains( d->m_routeList.at( position ).identifier() ) ) {
255                 d->m_routeList[ position ].setOnCloud( true );
256             }
257         }
258 
259         QStringList cachedRoutes;
260         for( const RouteItem &item: d->m_routeList ) {
261             cachedRoutes.append( item.identifier() );
262         }
263 
264         for( const RouteItem &item: routeList ) {
265             if( !cachedRoutes.contains( item.identifier() ) ) {
266                 d->m_routeList.append( item );
267             }
268         }
269     } else {
270         for( const RouteItem &item: routeList ) {
271             d->m_routeList.append( item );
272         }
273     }
274 
275     d->m_model->setItems( d->m_routeList );
276 }
277 
278 }
279 
280 #include "moc_RouteSyncManager.cpp"
281