First version. ADHERE service support with label, banner and media ads.
[qtmads] / src / qtmadsad.cpp
1 /*
2  * Copyright (c) 2009 Eetu Lehmusvuo.
3  *
4  * This file is part of QtMAds.
5  *
6  * QtMAds is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * QtMAds is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with QtMAds.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include <QtGui>
22
23 #include "qtmadsad.h"
24
25 #include <QApplication>
26 #include <QLabel>
27 #include <QBoxLayout>
28 #include <QPixmap>
29 #include <QUrl>
30 #include <QDebug>
31 #include <QDesktopServices>
32 #include <QWebView>
33 #include <QTimer>
34 #include <QDir>
35 #include <QPluginLoader>
36
37 #include "qtmadsservice.h"
38 #include "qtmadsserviceinterface.h"
39
40 QtmadsAd::QtmadsAd(QString serviceName, AdType defaultType, quint32 id, AdFit fitting)
41     : adType(anyAd), adFit(fitting)
42 {
43     qDebug() << "QtmadsAd()";
44     this->loadPlugin(serviceName);
45     this->service->initializeService(serviceName, id, defaultType);
46
47     layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
48
49     label = new QLabel(this);
50
51     htmlView = new QWebView(this);
52
53     this->image = 0;
54     adTimer = 0;
55     this->player = 0;
56 }
57
58 QtmadsAd::~QtmadsAd()
59 {
60     this->clear();
61 }
62
63 void QtmadsAd::startAd(quint32 adChangeIntervalInSecs)
64 {
65     getNewAd();
66
67     if(0 != adChangeIntervalInSecs){
68         if(adTimer == 0){
69             adTimer = new QTimer(this);
70             connect(adTimer, SIGNAL(timeout()), this, SLOT(getNewAd()));
71         }else{
72             adTimer->stop();
73         }
74         adTimer->start(adChangeIntervalInSecs*1000);
75     }
76 }
77
78 void QtmadsAd::getNewAd()
79 {
80     qDebug() << "getNewAd()";
81     connect(service, SIGNAL(adRequestReady(QHash<QString, QVariant> &)),this, SLOT(adRequestSucceeded(QHash<QString, QVariant> &)));
82     connect(service, SIGNAL(adRequestFailed()),this, SLOT(adRequestFailed()));
83
84     connect(this, SIGNAL(newAdReady()),this, SLOT(readyToShow()));
85     connect(this, SIGNAL(newAdFailed()),this, SLOT(showFailed()));
86
87     QHash<QString, QVariant> adParams;
88     this->getHash(adParams);
89     this->service->getAd(adParams);
90 }
91
92 void QtmadsAd::adRequestSucceeded(QHash<QString, QVariant> &ad)
93 {
94     qDebug() << "adRequestSucceeded()";
95     disconnect(service, SIGNAL(adRequestReady(QHash<QString, QVariant> &)),this, SLOT(adRequestSucceeded(QHash<QString, QVariant> &)));
96     disconnect(service, SIGNAL(adRequestFailed()),this, SLOT(adRequestFailed()));
97
98     bool adReady = true;
99
100     // clear previous ad
101     this->clear();
102
103     QHash<QString, QVariant>::iterator iter;
104
105     // get ad type before other
106     for(iter = ad.begin(); iter != ad.end(); iter++){
107         if(0 == iter.key().compare(TAG_ADTYPE)){
108             this->adType = this->service->getAdTypeFromString(iter.value().toString());
109             break;
110         }
111     }
112
113     for(iter = ad.begin(); iter != ad.end(); iter++){
114         //qDebug() << "key: " << iter.key() << " value: " << iter.value().toString();
115         if(0 == iter.key().compare(TAG_TEXT)){
116             this->text = iter.value().toString();
117
118         }else if(0 == iter.key().compare(TAG_CONTENT_URL)){
119             if(this->adType == imageAd){
120                 connect(service, SIGNAL(imageRequestReady(QImage *)),this, SLOT(imageRequestSucceeded(QImage *)));
121                 connect(service, SIGNAL(imageRequestFailed()),this, SLOT(imageRequestFailed()));
122                 this->service->getRemoteImage(iter.value().toString());
123                 adReady = false;
124             }
125             if(this->adType == videoAd || this->adType == audioAd ){
126                 this->mediaContentUrl = iter.value().toString();
127             }
128
129         }else if(0 == iter.key().compare(TAG_URL)){
130             adUrl.setUrl(iter.value().toString());
131
132         }else if(0 == iter.key().compare(TAG_HTML)){
133             this->htmlContent = iter.value().toString();
134
135         }else if(0 == iter.key().compare(TAG_ADTYPE)){
136             // already handled
137         }else{
138             qDebug() << "Unknown tag.";
139         }
140     }
141
142     if(adReady)
143         emit newAdReady();
144 }
145
146
147 void QtmadsAd::adRequestFailed()
148 {
149     disconnect(service, SIGNAL(adRequestReady(QHash<QString, QVariant> &)),this, SLOT(adRequestSucceeded(QHash<QString, QVariant> &)));
150     disconnect(service, SIGNAL(adRequestFailed()),this, SLOT(adRequestFailed()));
151
152     emit newAdFailed();
153 }
154
155
156 void QtmadsAd::imageRequestSucceeded(QImage *img)
157 {
158     disconnect(service, SIGNAL(imageRequestReady(QImage *)),this, SLOT(imageRequestSucceeded(QImage *)));
159     disconnect(service, SIGNAL(imageRequestFailed()),this, SLOT(imageRequestFailed()));
160
161     if(this->image != 0){
162         delete this->image;
163     }
164     this->image = img;
165
166     emit newAdReady();
167 }
168
169
170 void QtmadsAd::imageRequestFailed()
171 {
172     emit newAdFailed();
173 }
174
175
176 void QtmadsAd::getHash(QHash<QString, QVariant> &adParams)
177 {
178     adParams.insert("adsize", QVariant(this->sizeHint()));
179 }
180
181
182 void QtmadsAd::readyToShow()
183 {
184     qDebug() << "readyToShow()";
185     switch(this->adType){
186         default:
187         case txtAd:
188         {
189             this->label->setFont(this->font());
190             this->label->setAlignment(textAlignment);
191             this->label->setText(this->text);
192
193             if(this->adFit == fitAdToWidget){
194                 //TODO: fit text to ad
195                 /*QFontMetrics metrics (label->font());
196                 int pixelWidth = metrics.width(label->text());
197                 int pixelHeight = metrics.height();
198                 if()
199                 label->font().setPixelSize();*/
200             }/*TODO: else if(this->adFit == adToFit){
201             }*/
202
203             this->layout->addWidget(label);
204             label->show();
205             break;
206         }
207         case imageAd:
208         {
209             QPixmap pic;
210             if(this->adFit == fitAdToWidget){
211                 pic = QPixmap::fromImage(*image).scaled(this->size());
212             }else{
213                 pic = QPixmap::fromImage(*image);
214             }/*TODO: else if(this->adFit == fitWidgetToAd){
215             }*/
216
217             label->setPixmap(pic);
218             this->layout->addWidget(label);
219             label->show();
220             break;
221         }
222         case txtBannerAd:
223         case imageBannerAd:
224         {
225             /*TODO: if(this->adFit == fitToAd){
226
227             }else if(this->adFit == adToFit){
228             }*/
229             htmlView->setHtml(this->htmlContent);
230             htmlView->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
231             htmlView->setMinimumSize(QSize(216, 36));
232
233             //TODO: opening url inplace webpage
234             htmlView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
235
236             connect(htmlView->page(), SIGNAL(linkClicked(const QUrl &)),this, SLOT(adClicked(const QUrl &)));
237             this->layout->addWidget(htmlView);
238
239             htmlView->show();
240
241             break;
242         }
243         case audioAd:
244         case videoAd:
245         {
246             if(player){
247                 player->stop();
248             }
249             if(this->adType == audioAd){
250                 player = Phonon::createPlayer(Phonon::MusicCategory,
251                                      Phonon::MediaSource(this->mediaContentUrl));
252             }else{
253                 player = Phonon::createPlayer(Phonon::VideoCategory,
254                                          Phonon::MediaSource(this->mediaContentUrl));
255             }
256
257             if(player){
258                 connect(player, SIGNAL(stateChanged(Phonon::State newstate, Phonon::State oldstate)),
259                         this, SLOT(phononPlayerStateChanged(Phonon::State newstate, Phonon::State oldstate)));
260                 connect(player, SIGNAL(finished ()),
261                         this, SLOT(phononPlayerStateChanged()));
262
263                 player->play();
264
265                 qDebug() << "media play started!";
266             }
267             break;
268         }
269     }
270     disconnect(this, SIGNAL(newAdReady()),this, SLOT(readyToShow()));
271     disconnect(this, SIGNAL(newAdFailed()),this, SLOT(showFailed()));
272 }
273
274 void QtmadsAd::showFailed()
275 {
276     disconnect(this, SIGNAL(newAdReady()),this, SLOT(readyToShow()));
277     disconnect(this, SIGNAL(newAdFailed()),this, SLOT(showFailed()));
278
279     //TODO: emit signal for not able to show
280 }
281
282
283 void QtmadsAd::phononPlayerStateChanged(Phonon::State newstate, Phonon::State oldstate)
284 {
285     if(player){
286         if(newstate == Phonon::StoppedState || newstate == Phonon::ErrorState){
287             disconnect(player, SIGNAL(stateChanged(Phonon::State newstate, Phonon::State oldstate)),
288                     this, SLOT(phononState(Phonon::State newstate, Phonon::State oldstate)));
289             disconnect(player, SIGNAL(finished ()), this, SLOT(phononState()));
290             player->clear();
291             delete player;
292             player = 0;
293         }
294     }
295 }
296
297 void QtmadsAd::setAsTextAd(const QFont &font, bool isOnlyTextAd, Qt::Alignment alignment)
298 {
299     this->setFont(font);
300     this->textAlignment = alignment;
301
302     if(isOnlyTextAd){
303         this->service->setDefaultAdType(txtAd);
304     }else{
305         //TODO: HASH it
306         if(this->service->getDefaultAdType() != txtAd)
307             this->service->setDefaultAdType(anyAd);
308     }
309 }
310
311 void QtmadsAd::setAsImageAd(bool isOnlyImageAd)
312 {
313     if(isOnlyImageAd){
314         this->service->setDefaultAdType(imageAd);
315     }else{
316         //TODO: HASH it
317         if(this->service->getDefaultAdType() != imageAd)
318             this->service->setDefaultAdType(anyAd);
319     }
320 }
321
322 void QtmadsAd::setAsBannerAd(bool isOnlyBannerAd)
323 {
324     if(isOnlyBannerAd){
325         this->service->setDefaultAdType(imageBannerAd);
326     }else{
327         //TODO: HASH it
328         if(this->service->getDefaultAdType() != imageBannerAd)
329             this->service->setDefaultAdType(anyAd);
330     }
331 }
332
333
334 void QtmadsAd::setAsTextBannerAd(bool isOnlyTextBannerAd)
335 {
336     if(isOnlyTextBannerAd){
337         this->service->setDefaultAdType(txtBannerAd);
338     }else{
339         //TODO: HASH it
340         if(this->service->getDefaultAdType() != txtBannerAd)
341             this->service->setDefaultAdType(anyAd);
342     }
343 }
344
345 void QtmadsAd::setAsAudioAd(bool isOnlyAudioAd)
346 {
347     if(isOnlyAudioAd){
348         this->service->setDefaultAdType(audioAd);
349     }else{
350         //TODO: HASH it
351         if(this->service->getDefaultAdType() != audioAd)
352             this->service->setDefaultAdType(anyAd);
353     }
354 }
355
356 void QtmadsAd::setAsVideoAd()
357 {
358     this->service->setDefaultAdType(videoAd);
359 }
360
361 void QtmadsAd::adClicked(const QUrl &url)
362 {
363     if(url.isValid()){
364         if(!adUrl.isValid()){
365             qDebug() << "Browser opened to urli: " << url.toString();
366             QDesktopServices::openUrl(url);
367         }
368     }else{
369         qDebug() << "Browser opened to urli: " << adUrl.toString();
370         QDesktopServices::openUrl(adUrl);
371     }
372 }
373
374 void QtmadsAd::clear()
375 {
376     qDebug() << "clear()";
377     adID = -1;
378     text.clear();
379     if(image != 0){
380         delete image;
381         image = 0;
382     }
383     imageUrl.clear();
384
385     adUrl.clear();
386     disconnect(htmlView->page(), SIGNAL(linkClicked(const QUrl &)),this, SLOT(adClicked(const QUrl &)));
387
388     //TODO: remove widgets from layout?
389     label->clear();
390     htmlView->close();
391
392     if(this->player){
393         disconnect(player, SIGNAL(stateChanged(Phonon::State newstate, Phonon::State oldstate)),
394                 this, SLOT(phononState(Phonon::State newstate, Phonon::State oldstate)));
395         disconnect(player, SIGNAL(finished ()), this, SLOT(phononState()));
396         player->clear();
397         delete player;
398         player = 0;
399     }
400 }
401
402 bool QtmadsAd::loadPlugin(QString serviceName)
403 {
404     bool pluginFound = false;
405     QDir pluginsDir(qApp->applicationDirPath());
406 #if defined(Q_OS_WIN)
407     if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
408         pluginsDir.cdUp();
409 #elif defined(Q_OS_MAC)
410     if (pluginsDir.dirName() == "MacOS") {
411         pluginsDir.cdUp();
412         pluginsDir.cdUp();
413         pluginsDir.cdUp();
414     }
415 #endif
416     pluginsDir.cd(PLUGINS_PATH);
417     foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
418         qDebug() << "path: " << pluginsDir.absoluteFilePath(fileName);
419         if(fileName.contains(serviceName) && !pluginFound){
420             QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
421             QObject *plugin = pluginLoader.instance();
422             if (plugin) {
423                 QtmadsServiceInterface *plugLoader = qobject_cast<QtmadsServiceInterface *>(plugin);
424                 this->service = plugLoader->loadService();
425                 if (this->service)
426                     pluginFound = true;
427                 else{
428                     qDebug() << "Plugin load failed: " << pluginLoader.errorString();
429                     }
430                 plugLoader = 0;
431                 delete plugin;
432             }else{
433                 qDebug() << "Plugin load failed: " << pluginLoader.errorString();
434             }
435         }else{
436             qDebug() << "not service";
437         }
438     }
439
440     return pluginFound;
441
442 }
443
444