Architecture changed to allow easier addition of new phone books. Norwegian phonebook...
[jenirok] / src / daemon / calllistener.cpp
1 /*
2  * This file is part of Jenirok.
3  *
4  * Jenirok is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Jenirok is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Jenirok.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  */
18
19 #include <QtCore/QDebug>
20 #include <QtCore/QTimer>
21 #include "calllistener.h"
22 #include "settings.h"
23 #include "cache.h"
24 #include "contactmanager.h"
25 #include "connectionmanager.h"
26 #include "sourcecoreconfig.h"
27 #include "db.h"
28
29 namespace
30 {
31     const QString CALL_SERVICE_NAME = "com.nokia.csd";
32     const QString CALL_SERVICE_PATH = "/com/nokia/csd/call";
33     const QString CALL_SERVICE_INTERFACE = "com.nokia.csd.Call";
34     const QString CALL_SERVICE_INSTANCE_NAME = "com.nokia.csd.Call.Instance";
35     const QString CALL_SIGNAL_INCOMING = "Coming";
36     const QString CALL_SIGNAL_RELEASE = "Release";
37     const QString CALL_SIGNAL_TERMINATED = "Terminated";
38 }
39
40 QDBusConnection CallListener::systemBus_ = QDBusConnection::systemBus();
41
42 CallListener::CallListener(): source_(0),
43 closeConnection_(false), initialized_(false), box_(0), label_(0),
44 retries_(-1), timer_(0)
45 {
46 }
47
48 CallListener::~CallListener()
49 {
50     end();
51 }
52
53 bool CallListener::begin()
54 {
55     if(Settings::instance()->getConnectionType() == Settings::ALWAYS_ASK)
56     {
57         qDebug() << "Bad connection settings, unable to start";
58         return false;
59     }
60
61     sourceId_ = Source::stringToId(Settings::instance()->get("source"));
62     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
63     config->getConfig(sourceConfig_);
64     delete config;
65
66     systemBus_.connect(CALL_SERVICE_NAME,
67                        CALL_SERVICE_PATH,
68                        CALL_SERVICE_INTERFACE,
69                        CALL_SIGNAL_INCOMING,
70                        this,
71                        SLOT(incomingCall(QDBusObjectPath, QString)));
72
73     systemBus_.connect(CALL_SERVICE_NAME,
74                        CALL_SERVICE_PATH,
75                        CALL_SERVICE_INTERFACE,
76                        CALL_SIGNAL_RELEASE,
77                        this,
78                        SLOT(callTerminate()));
79
80
81     qDebug() << "Starting...";
82
83     return true;
84
85 }
86
87 void CallListener::end()
88 {
89     systemBus_.disconnect(CALL_SERVICE_NAME,
90                           CALL_SERVICE_PATH,
91                           CALL_SERVICE_INTERFACE,
92                           CALL_SIGNAL_INCOMING,
93                           this,
94                           SLOT(incomingCall(QDBusObjectPath, QString)));
95
96     systemBus_.disconnect(CALL_SERVICE_NAME,
97                           CALL_SERVICE_PATH,
98                           CALL_SERVICE_INTERFACE,
99                           CALL_SIGNAL_RELEASE,
100                           this,
101                           SLOT(callTerminate()));
102
103     searchClose();
104     sourceConfig_.clear();
105
106 }
107
108 void CallListener::search(Source::SearchDetails const& details)
109 {
110     qDebug() << "Search called";
111
112     searchInit();
113
114     Source::Result result;
115
116     if(Cache::instance().findItem(details.query, result))
117     {
118
119         qDebug() << "Found from cache";
120
121         showDelayedResult(createResult(result.name,
122                                        result.street,
123                                        result.city), BANNER_DELAY);
124     }
125     else
126     {
127         retries_ = 0;
128         currentSearch_ = details.query;
129
130         if(!handleConnection())
131         {
132             qDebug() << "Unable to connect";
133             return;
134         }
135
136         showDelayedResult(tr("Searching..."), BANNER_DELAY);
137
138         qDebug() << "Starting to search...";
139
140         source_->search(details);
141     }
142
143 }
144
145 void CallListener::requestFinished(QVector <Source::Result> const& results,
146                                    Source::SearchDetails const& details,
147                                    bool error)
148 {
149     // If box is not visible, the call must have been terminated already
150     if(!initialized_ || !box_->isVisible())
151     {
152         return;
153     }
154
155     QString message;
156
157     if(error)
158     {
159         qDebug() << "Error: " << source_->errorString();
160
161         if(retries_ < SEARCH_RETRIES && retries_ >= 0)
162         {
163             retries_++;
164             source_->search(Source::SearchDetails(currentSearch_));
165             return;
166         }
167         else
168         {
169             timedMessage_ = "";
170             QString errorString;
171             Source::Error error = source_->error();
172
173             switch(error)
174             {
175             case Source::TIMEOUT:
176                 errorString = tr("Request timed out");
177                 break;
178             default:
179                 errorString = source_->errorString();
180                 break;
181             }
182
183             showError(tr("Searching failed:") + " " + errorString + ".");
184         }
185     }
186     else
187     {
188         timedMessage_ = "";
189
190         if(results.size() == 0)
191         {
192             message = tr("Phone number was not found");
193             showResult(message);
194         }
195         else
196         {
197             message = createResult(results.at(0).name, results.at(0).street,
198                                    results.at(0).city);
199             showResult(message);
200             Source::Result result = results.at(0);
201             result.number = details.query;
202             Cache::instance().addItem(result);
203         }
204     }
205
206     retries_ = -1;
207     currentSearch_ = "";
208
209     if(closeConnection_)
210     {
211         ConnectionManager cm;
212         cm.disconnect(true);
213         closeConnection_ = false;
214     }
215 }
216
217 QString CallListener::createResult(QString const& name, QString const& street, QString const& city)
218 {
219     QString result = "<b>" + name + "</b>";
220
221     if(!street.isEmpty() || !city.isEmpty())
222     {
223         result += "<br>";
224
225         if(!street.isEmpty())
226         {
227             result += street + ", ";
228         }
229
230         result += city;
231     }
232
233     return result;
234 }
235
236 void CallListener::showResult(QString const& text)
237 {
238     if(!initialized_)
239     {
240         return;
241     }
242
243     label_->setText("<font color='black'>" + text + "</font>");
244
245     if(box_->isVisible())
246     {
247         box_->hide();
248     }
249
250     box_->show();
251 }
252
253 void CallListener::incomingCall(QDBusObjectPath path, QString number)
254 {
255     qDebug() << "Incoming: " << number;
256
257     ContactManager cm;
258
259     if(!cm.numberExists(number))
260     {
261         qDebug() << "Number doesn't exist";
262
263         systemBus_.connect(CALL_SERVICE_NAME,
264                            path.path(),
265                            CALL_SERVICE_INSTANCE_NAME,
266                            CALL_SIGNAL_TERMINATED,
267                            this,
268                            SLOT(callTerminate()));
269
270         search(Source::SearchDetails(number));
271     }
272     else
273     {
274         qDebug() << "Number exists";
275     }
276 }
277
278 void CallListener::callTerminate()
279 {
280     if(box_ && box_->isVisible())
281     {
282         box_->hide();
283     }
284
285     if(closeConnection_)
286     {
287         ConnectionManager cm;
288         cm.disconnect(true);
289         closeConnection_ = false;
290     }
291
292     searchClose();
293 }
294
295 void CallListener::showDelayedResult(QString const& text, int delay)
296 {
297     timedMessage_ = text;
298     QTimer::singleShot(delay, this, SLOT(showTimedMessage()));
299 }
300
301 void CallListener::showTimedMessage()
302 {
303     if(timedMessage_.size() == 0 || !initialized_)
304     {
305         return;
306     }
307
308     showResult(timedMessage_);
309
310     timedMessage_ = "";
311 }
312
313 void CallListener::searchInit()
314 {
315     qDebug() << "Initializing search...";
316
317     if(initialized_)
318     {
319         qDebug() << "Already initialized";
320         return;
321     }
322
323     source_ = Source::getSource(sourceId_);
324     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
325     config->loadFromConfig(sourceConfig_);
326     config->apply(source_);
327     delete config;
328     source_->setMaxResults(1);
329     source_->setFindNumber(false);
330     source_->setTimeout(REQUEST_TIMEOUT);
331
332     connect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
333                                            Source::SearchDetails const&, bool)),
334                                            this, SLOT(requestFinished(QVector <Source::Result> const&,
335                                                                       Source::SearchDetails const&, bool)));
336     box_ = new InformationBox;
337     label_ = new QLabel("", box_);
338     label_->setMargin(8);
339     label_->setWordWrap(true);
340     box_->setWidget(label_);
341     initialized_ = true;
342 }
343
344 void CallListener::searchClose()
345 {
346     initialized_ = false;
347
348     qDebug() << "Closing search...";
349
350     if(source_)
351     {
352         disconnect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
353                                                   Source::SearchDetails const&, bool)),
354                                                   this, SLOT(requestFinished(QVector <Source::Result> const&,
355                                                                              Source::SearchDetails const&, bool)));
356     }
357
358     delete source_;
359     source_ = 0;
360     delete box_;
361     box_ = 0;
362     label_ = 0;
363 }
364
365 bool CallListener::handleConnection()
366 {
367     if(!initialized_)
368     {
369         return false;
370     }
371
372     ConnectionManager cm;
373
374     if(cm.isConnected())
375     {
376         closeConnection_ = false;
377         return true;
378     }
379
380     closeConnection_ = true;
381
382     Settings::ConnectionType configType = Settings::instance()->getConnectionType();
383
384     if(configType == Settings::ALWAYS_ASK)
385     {
386         showError(tr("Automatic connecting is not allowed by settings."), BANNER_DELAY);
387         return false;
388     }
389
390     showDelayedResult(tr("Connecting..."), BANNER_DELAY);
391
392     ConnectionManager::Connection best;
393
394     ConnectionManager::ConnectionType lookupType = ConnectionManager::NO_TYPE;
395
396     switch(configType)
397     {
398     case Settings::WLAN:
399         lookupType = ConnectionManager::WLAN;
400         break;
401     case Settings::GPRS:
402         lookupType = ConnectionManager::GPRS;
403         break;
404     default:
405         lookupType = ConnectionManager::NO_TYPE;
406         break;
407     }
408
409     int cretries = 0;
410
411     while(cretries < CONNECTION_LOOKUP_RETRIES)
412     {
413         if(!initialized_)
414         {
415             return false;
416         }
417
418         if(cm.getBestConnection(best, lookupType))
419         {
420             break;
421         }
422
423         qDebug() << "No connections found, retrying...";
424
425         cretries++;
426
427         sleep(WAIT_BETWEEN_RETRIES);
428
429     }
430
431     if(cretries >= CONNECTION_LOOKUP_RETRIES)
432     {
433         showError(tr("No available 3G or WLAN networks found."));
434         return false;
435     }
436
437     int retries = 0;
438
439     while(retries < CONNECT_RETRIES)
440     {
441         if(!initialized_)
442         {
443             return false;
444         }
445
446         sleep(WAIT_BETWEEN_RETRIES);
447
448         if(cm.connect(best.id))
449         {
450             break;
451         }
452         else if(cm.error() == ConnectionManager::INVALID_IAP)
453         {
454             showError(tr("Selected access point doesn't exist."));
455             return false;
456         }
457
458         qDebug() << "Unable to connect, retrying...";
459         retries++;
460
461     }
462
463     if(initialized_ && retries >= CONNECT_RETRIES)
464     {
465         showError(tr("Unable to connect to network."));
466         return false;
467     }
468
469     return initialized_;
470 }
471
472 void CallListener::showError(QString const& msg, int timeout)
473 {
474     qDebug() << "Error: " << msg;
475     box_->setTimeout(ERROR_BANNER_TIMEOUT);
476
477     if(timeout)
478     {
479         showDelayedResult(msg, timeout);
480     }
481     else
482     {
483         showResult(msg);
484     }
485 }
486
487 void CallListener::timerEvent(QTimerEvent* event)
488 {
489     Q_UNUSED(event);
490     killTimer(timer_);
491     timer_ = 0;
492 }
493
494 void CallListener::sleep(int ms)
495 {
496     if(timer_)
497     {
498         killTimer(timer_);
499     }
500
501     timer_ = startTimer(ms);
502
503     while(timer_)
504     {
505         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
506     }
507 }