Added First version of MeeGo Harmattan port from pure QT to MTF.
[medard] / src / medarddownloader.cpp
1 /*
2  *  Medard for Maemo.
3  *  Copyright (C) 2011 Roman Moravcik
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <QTimer>
21 #include <QImage>
22 #include <QDir>
23 #include <QFile>
24
25 #include "medarddownloader.h"
26
27 #define DOWNLOAD_CACHE_DIR ".cache/medard-downloader"
28
29 #define MEDARD_URL "http://www.medard-online.cz"
30 #define MEDARD_IMAGE_URL "http://www.medard-online.cz/scripts/getimage.php?initDate=%1&domain=%2&variable=%3&offset=%4"
31
32 #define FC_SEA_LEVEL_PRESSURE "slp"
33 #define FC_PRECIPITATION "precip"
34 #define FC_WIND_VELOCITY "wv"
35 #define FC_CLOUDINESS "cloud"
36 #define FC_TEMPERATURE "temp"
37
38 #define FD_EUROPE "1"
39 #define FD_CZECH_REPUBLIC "2"
40
41 #define MIN_OFFSET 0
42 #define MAX_OFFSET 72
43
44 #define IMAGE_WIDTH 556
45 #ifdef Q_WS_MAEMO_6
46 #define IMAGE_HEIGHT 369
47 #else
48 #define IMAGE_HEIGHT 408
49 #endif
50
51 #define MAX_DOWNLOAD_RETRIES 3
52 #define RETRY_TIMEOUT 5000
53
54 MedardDownloader::MedardDownloader() : QObject()
55 {
56     m_forecastType = FC_SEA_LEVEL_PRESSURE;
57     m_forecastDomain = FD_EUROPE;
58     m_forecastInitialDateCode.clear();
59     m_forecastDateOffset = 0;
60
61     m_network = new QNetworkAccessManager();
62     m_reply = 0;
63
64     m_retryCounter = 0;
65     m_retryTimer = new QTimer();
66     connect(m_retryTimer, SIGNAL(timeout()), this, SLOT(retryTimerEvent()));
67
68     m_cachePath = QString("%1/%2")
69                   .arg(QDir().homePath())
70                   .arg(DOWNLOAD_CACHE_DIR);
71
72     createCacheDirectory();
73 }
74
75 MedardDownloader::~MedardDownloader()
76 {
77     if (m_retryTimer->isActive())
78         m_retryTimer->stop();
79
80     if (m_reply) {
81         m_reply->abort();
82         delete m_reply;
83     }
84 }
85
86 QSize MedardDownloader::imageSize()
87 {
88     return QSize(IMAGE_WIDTH, IMAGE_HEIGHT);
89 }
90
91 void MedardDownloader::setForecastType(ForecastType type)
92 {
93     switch (type) {
94         case SeaLevelPressure:
95             m_forecastType = FC_SEA_LEVEL_PRESSURE;
96             break;
97
98         case Precipitation:
99             m_forecastType = FC_PRECIPITATION;
100             break;
101
102         case WindVelocity:
103             m_forecastType = FC_WIND_VELOCITY;
104             break;
105
106         case Cloudiness:
107             m_forecastType = FC_CLOUDINESS;
108             break;
109
110         case Temperature:
111             m_forecastType = FC_TEMPERATURE;
112             break;
113     }
114 }
115
116 void MedardDownloader::setForecastDomain(ForecastDomain domain)
117 {
118     switch (domain) {
119         case Europe:
120             m_forecastDomain = FD_EUROPE;
121             break;
122
123         case CzechRepublic:
124             m_forecastDomain = FD_CZECH_REPUBLIC;
125             break;
126     }
127 }
128
129 void MedardDownloader::setForecastInitialDate(QDateTime date)
130 {
131     QString offset;
132
133     m_forecastInitialDate = date.toUTC();
134
135     if (date.toUTC().time().hour() >= 18) {
136         m_forecastInitialDate.setTime(QTime(18, 0, 0));
137         offset = "12";
138     } else if (date.toUTC().time().hour() >= 12) {
139         m_forecastInitialDate.setTime(QTime(12, 0, 0));
140         offset = "06";
141     } else if (date.toUTC().time().hour() >= 6) {
142         m_forecastInitialDate.setTime(QTime(6, 0, 0));
143         offset = "00";
144     } else {
145         m_forecastInitialDate.setTime(QTime(0, 0, 0));
146         offset = "18";
147     }
148
149     if (offset == "18") {
150         /* use previous day */
151         m_forecastInitialDateCode = QString("%1_%2")
152                                     .arg(date.addDays(-1).toUTC().toString("yyMMdd"))
153                                     .arg(offset);
154     } else {
155         /* use current day */
156         m_forecastInitialDateCode = QString("%1_%2")
157                                     .arg(date.toUTC().toString("yyMMdd"))
158                                     .arg(offset);
159     }
160
161     cleanCacheDirectory();
162 }
163
164 QDateTime MedardDownloader::forecastInitialDate()
165 {
166     return m_forecastInitialDate.toLocalTime();
167 }
168
169 QDateTime MedardDownloader::forecastDate()
170 {
171     return m_forecastInitialDate.addSecs(3600 * m_forecastDateOffset).toLocalTime();
172 }
173
174 void MedardDownloader::setForecastDateOffset(int offset)
175 {
176     m_forecastDateOffset = offset;
177     if (m_forecastDateOffset > MAX_OFFSET)
178         m_forecastDateOffset = MAX_OFFSET;
179
180     if (m_forecastDateOffset < MIN_OFFSET)
181         m_forecastDateOffset = MIN_OFFSET;
182 }
183
184 void MedardDownloader::retryTimerEvent()
185 {
186     if (m_retryTimer->isActive())
187         m_retryTimer->stop();
188
189     downloadImage();
190 }
191
192 void MedardDownloader::tryDownloadImageAgain()
193 {
194     m_retryCounter++;
195
196     if (m_retryCounter < MAX_DOWNLOAD_RETRIES) {
197         m_retryTimer->setInterval(RETRY_TIMEOUT * (m_retryCounter + 1));
198         m_retryTimer->start();
199     } else {
200         m_retryCounter = 0;
201         emit downloadFailed();
202     }
203 }
204
205 void MedardDownloader::clearDownloadRequest()
206 {
207     delete m_reply;
208     m_reply = 0;
209 }
210
211 void MedardDownloader::downloadImageFinished()
212
213     QByteArray picture = m_reply->readAll();
214
215     if (picture.isNull() || picture.size() <= 0)
216         return;
217
218     m_retryCounter = 0;
219
220     QImage image;
221     if (!image.loadFromData(picture, "png"))
222         return;
223
224     QString filename = QString("%1/%2_%3_%4_%5.png")
225                        .arg(m_cachePath)
226                        .arg(m_forecastType)
227                        .arg(m_forecastDomain)
228                        .arg(m_forecastInitialDateCode)
229                        .arg(QString().number(m_forecastDateOffset));
230
231     if ((image.width() == 512) && (image.height() == 512)) {
232         QImage croped(512, 400, QImage::Format_ARGB32_Premultiplied);
233         croped = image.copy(0, 52, 512, IMAGE_HEIGHT);
234         croped.save(filename, "png");
235     } else {
236         QImage croped(560, 400, QImage::Format_ARGB32_Premultiplied);
237         croped = image.copy(10, 96, IMAGE_WIDTH, IMAGE_HEIGHT);
238         croped.save(filename, "png");
239     }
240
241     emit downloadFinished(filename, forecastDate());
242
243     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
244 }
245
246 void MedardDownloader::downloadImageError(QNetworkReply::NetworkError /* code */)
247 {
248     tryDownloadImageAgain();
249     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
250 }
251
252 void MedardDownloader::downloadImage()
253 {
254     if (m_forecastInitialDateCode.isNull()) {
255         retrieveForecastInitialDate();
256         tryDownloadImageAgain();
257         return;
258     }
259
260     QString filename = QString("%1/%2_%3_%4_%5.png")
261                        .arg(m_cachePath)
262                        .arg(m_forecastType)
263                        .arg(m_forecastDomain)
264                        .arg(m_forecastInitialDateCode)
265                        .arg(QString().number(m_forecastDateOffset));
266
267     if (isDownloaded(filename)) {
268         emit downloadFinished(filename, forecastDate());
269         return;
270     }
271
272     QString imageUrl = QString(MEDARD_IMAGE_URL)
273                                .arg(m_forecastInitialDateCode)
274                                .arg(m_forecastDomain)
275                                .arg(m_forecastType)
276                                .arg(QString().number(m_forecastDateOffset));
277
278     QUrl url(imageUrl);
279     QNetworkRequest request(url);
280
281     if (m_reply)
282         clearDownloadRequest();
283     m_reply = m_network->get(request);
284
285     connect(m_reply, SIGNAL(finished()), this, SLOT(downloadImageFinished()));
286     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
287             SLOT(downloadImageError(QNetworkReply::NetworkError)));
288 }
289
290 bool MedardDownloader::isDownloaded(const QString filename)
291 {
292     return QFile(filename).exists();
293 }
294
295 int MedardDownloader::forecastDateOffset()
296 {
297     return m_forecastDateOffset;
298 }
299
300 int MedardDownloader::minForecastDateOffset()
301 {
302     return MIN_OFFSET;
303 }
304
305 int MedardDownloader::maxForecastDateOffset()
306 {
307     return MAX_OFFSET;
308 }
309
310 void MedardDownloader::retrieveForecastInitialDateFinished()
311 {
312     QByteArray data = m_reply->readAll();
313
314     int index1 = data.indexOf("var fcst_initDatestamp=\"", 0);
315     int index2 = data.indexOf("\";", index1);
316     if (index1 != -1) {
317         QString temp;
318         for (int i = index1 + 24; i < index2; i++) {
319             temp.append(data.at(i));
320         }
321         QDateTime date = QDateTime::fromTime_t(temp.toULong() + 6 * 3600);
322         if (!date.isNull()) {
323             setForecastInitialDate(date.toLocalTime());
324
325             int forecastDateOffset = date.toLocalTime().secsTo(QDateTime().currentDateTime()) / 3600;
326             setForecastDateOffset(forecastDateOffset);
327         }
328         m_retryCounter = 0;
329     }
330
331     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
332 }
333
334 void MedardDownloader::retrieveForecastInitialDateError(QNetworkReply::NetworkError /* code */)
335 {
336 }
337
338 void MedardDownloader::retrieveForecastInitialDate()
339 {
340     QString serverUrl = QString(MEDARD_URL);
341
342     QUrl url(serverUrl);
343     QNetworkRequest request(url);
344
345     if (m_reply)
346         clearDownloadRequest();
347     m_reply = m_network->get(request);
348
349     connect(m_reply, SIGNAL(finished()), this, SLOT(retrieveForecastInitialDateFinished()));
350     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
351             SLOT(retrieveForecastInitialDateError(QNetworkReply::NetworkError)));
352 }
353
354 void MedardDownloader::createCacheDirectory()
355 {
356     QDir cacheDir(m_cachePath);
357     if (!cacheDir.exists())
358         cacheDir.mkpath(cacheDir.path());
359 }
360
361 void MedardDownloader::cleanCacheDirectory()
362 {
363     QDir cacheDir(m_cachePath);
364     QStringList list = cacheDir.entryList();
365     for (int i = 0; i < list.size(); i++) {
366         if (!list.at(i).contains(m_forecastInitialDateCode)) {
367             QFile(m_cachePath + "/" + list.at(i)).remove();
368         }
369     }
370 }