Changed daemon to search both people and companies instead of only people. Tuned...
[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     DB::removeDatabase();
52 }
53
54 bool CallListener::begin()
55 {
56     if(Settings::instance()->getConnectionType() == Settings::ALWAYS_ASK)
57     {
58         qDebug() << "Bad connection settings, unable to start";
59         return false;
60     }
61
62     sourceId_ = Source::stringToId(Settings::instance()->get("source"));
63     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
64     config->getConfig(sourceConfig_);
65     delete config;
66
67     systemBus_.connect(CALL_SERVICE_NAME,
68                        CALL_SERVICE_PATH,
69                        CALL_SERVICE_INTERFACE,
70                        CALL_SIGNAL_INCOMING,
71                        this,
72                        SLOT(incomingCall(QDBusObjectPath, QString)));
73
74     systemBus_.connect(CALL_SERVICE_NAME,
75                        CALL_SERVICE_PATH,
76                        CALL_SERVICE_INTERFACE,
77                        CALL_SIGNAL_RELEASE,
78                        this,
79                        SLOT(callTerminate()));
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(closeConnection_)
150     {
151         closeConnection_ = false;
152         ConnectionManager cm;
153         cm.disconnect(true);
154     }
155
156     // If box is not visible, the call must have been terminated already
157     if(!initialized_ || !box_->isVisible())
158     {
159         return;
160     }
161
162     QString message;
163
164     if(error)
165     {
166         qDebug() << "Error: " << source_->errorString();
167
168         if(retries_ < SEARCH_RETRIES && retries_ >= 0)
169         {
170             retries_++;
171             source_->search(Source::SearchDetails(currentSearch_, "", Source::BOTH));
172             return;
173         }
174         else
175         {
176             timedMessage_ = "";
177             QString errorString;
178             Source::Error error = source_->error();
179
180             switch(error)
181             {
182             case Source::TIMEOUT:
183                 errorString = tr("Request timed out");
184                 break;
185             default:
186                 errorString = source_->errorString();
187                 break;
188             }
189
190             showError(tr("Searching failed:") + " " + errorString + ".");
191         }
192     }
193     else
194     {
195         timedMessage_ = "";
196
197         if(results.size() == 0)
198         {
199             message = tr("Phone number was not found");
200             showResult(message);
201         }
202         else
203         {
204             message = createResult(results.at(0).name, results.at(0).street,
205                                    results.at(0).city);
206             showResult(message);
207             Source::Result result = results.at(0);
208             result.number = details.query;
209             Cache::instance().addItem(result);
210         }
211     }
212
213     retries_ = -1;
214     currentSearch_ = "";
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     ContactManager cm;
256
257     if(!cm.numberExists(number))
258     {
259         qDebug() << "Number doesn't exist";
260
261         systemBus_.connect(CALL_SERVICE_NAME,
262                            path.path(),
263                            CALL_SERVICE_INSTANCE_NAME,
264                            CALL_SIGNAL_TERMINATED,
265                            this,
266                            SLOT(callTerminate()));
267
268         search(Source::SearchDetails(number, "", Source::BOTH));
269     }
270     else
271     {
272         qDebug() << "Number exists";
273     }
274 }
275
276 void CallListener::callTerminate()
277 {
278     if(box_ && box_->isVisible())
279     {
280         box_->hide();
281     }
282
283     searchClose();
284 }
285
286 void CallListener::showDelayedResult(QString const& text, int delay)
287 {
288     timedMessage_ = text;
289     QTimer::singleShot(delay, this, SLOT(showTimedMessage()));
290 }
291
292 void CallListener::showTimedMessage()
293 {
294     if(timedMessage_.size() == 0 || !initialized_)
295     {
296         return;
297     }
298
299     showResult(timedMessage_);
300
301     timedMessage_ = "";
302 }
303
304 void CallListener::searchInit()
305 {
306     qDebug() << "Initializing search...";
307
308     if(initialized_)
309     {
310         qDebug() << "Already initialized";
311         return;
312     }
313
314     source_ = Source::getSource(sourceId_);
315     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
316     config->loadFromConfig(sourceConfig_);
317     config->apply(source_);
318     delete config;
319     source_->setMaxResults(1);
320     source_->setFindNumber(false);
321     source_->setTimeout(REQUEST_TIMEOUT);
322
323     connect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
324                                            Source::SearchDetails const&, bool)),
325                                            this, SLOT(requestFinished(QVector <Source::Result> const&,
326                                                                       Source::SearchDetails const&, bool)));
327     box_ = new InformationBox;
328     label_ = new QLabel("", box_);
329     label_->setMargin(8);
330     label_->setWordWrap(true);
331     box_->setWidget(label_);
332     initialized_ = true;
333 }
334
335 void CallListener::searchClose()
336 {
337     if(!initialized_)
338     {
339         return;
340     }
341
342     initialized_ = false;
343
344     qDebug() << "Closing search...";
345
346     if(source_)
347     {
348         disconnect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
349                                                   Source::SearchDetails const&, bool)),
350                                                   this, SLOT(requestFinished(QVector <Source::Result> const&,
351                                                                              Source::SearchDetails const&, bool)));
352     }
353
354     delete source_;
355     source_ = 0;
356     delete box_;
357     box_ = 0;
358     label_ = 0;
359
360     if(closeConnection_)
361     {
362         closeConnection_ = false;
363         ConnectionManager cm;
364         cm.disconnect(true);
365     }
366 }
367
368 bool CallListener::handleConnection()
369 {
370     if(!initialized_)
371     {
372         return false;
373     }
374
375     ConnectionManager cm;
376
377     if(cm.isConnected())
378     {
379         closeConnection_ = false;
380         return true;
381     }
382
383     closeConnection_ = true;
384
385     Settings::ConnectionType configType = Settings::instance()->getConnectionType();
386
387     if(configType == Settings::ALWAYS_ASK)
388     {
389         showError(tr("Automatic connecting is not allowed by settings."), BANNER_DELAY);
390         return false;
391     }
392
393     showDelayedResult(tr("Connecting..."), BANNER_DELAY);
394
395     ConnectionManager::Connection best;
396
397     ConnectionManager::ConnectionType lookupType = ConnectionManager::NO_TYPE;
398
399     switch(configType)
400     {
401     case Settings::WLAN:
402         lookupType = ConnectionManager::WLAN;
403         break;
404     case Settings::GPRS:
405         lookupType = ConnectionManager::GPRS;
406         break;
407     default:
408         lookupType = ConnectionManager::NO_TYPE;
409         break;
410     }
411
412     int cretries = 0;
413     int scans = 0;
414     bool found = false;
415     int maxScans = GPRS_SCANS;
416
417     if(lookupType != ConnectionManager::GPRS)
418     {
419         maxScans = WLAN_SCANS;
420     }
421
422     while(cretries < CONNECTION_LOOKUP_RETRIES)
423     {
424         if(!initialized_)
425         {
426             return false;
427         }
428
429         if(scans < maxScans)
430         {
431             if(cm.getBestConnection(best, lookupType))
432             {
433                 found = true;
434             }
435
436             scans++;
437         }
438
439         // If there is only gprs connection available,
440         // make sure that we are on 3g network
441         if(found && (best.type != ConnectionManager::GPRS || is3g()))
442         {
443             break;
444         }
445
446         if(found)
447         {
448             sleep(WAIT_BETWEEN_RETRIES);
449         }
450
451         qDebug() << "No connections found, retrying...";
452
453         cretries++;
454
455     }
456
457     if(cretries >= CONNECTION_LOOKUP_RETRIES)
458     {
459         showError(tr("No available 3G or WLAN networks found."));
460         return false;
461     }
462
463     int retries = 0;
464
465     while(retries < CONNECT_RETRIES)
466     {
467         if(!initialized_)
468         {
469             return false;
470         }
471
472         qDebug() << "Connecting to " << best.name;
473
474         if(cm.connect(best.id))
475         {
476             break;
477         }
478         else if(cm.error() == ConnectionManager::INVALID_IAP)
479         {
480             showError(tr("Selected access point doesn't exist."));
481             return false;
482         }
483
484         retries++;
485
486         qDebug() << "Unable to connect, retrying...";
487
488         if(retries < CONNECT_RETRIES)
489         {
490             sendRetrySignal(best.id, initialized_);
491         }
492
493     }
494
495     if(retries >= CONNECT_RETRIES)
496     {
497         sendRetrySignal(best.id, false);
498
499         if(initialized_)
500         {
501             showError(tr("Unable to connect to network."));
502         }
503
504         return false;
505     }
506
507     return initialized_;
508 }
509
510 void CallListener::showError(QString const& msg, int timeout)
511 {
512     qDebug() << "Error: " << msg;
513
514     if(!initialized_ || !box_)
515     {
516         return;
517     }
518
519     box_->setTimeout(ERROR_BANNER_TIMEOUT);
520
521     if(timeout)
522     {
523         showDelayedResult(msg, timeout);
524     }
525     else
526     {
527         showResult(msg);
528     }
529 }
530
531 bool CallListener::is3g()
532 {
533     QDBusMessage msg = QDBusMessage::createMethodCall("com.nokia.phone.net",
534                                                       "/com/nokia/phone/net",
535                                                       "Phone.Net",
536                                                       "get_registration_status");
537
538     QDBusMessage rep = systemBus_.call(msg);
539
540     if(rep.type() == QDBusMessage::ErrorMessage)
541     {
542         qDebug() << "Unable to get network status";
543         return false;
544     }
545
546     uint status = rep.arguments().value(6).toUInt();
547
548     if(status & 0x10 || status & 0x08)
549     {
550         return true;
551     }
552
553     return false;
554 }
555
556 void CallListener::sendRetrySignal(QString const& iap, bool retry)
557 {
558     QDBusMessage msg = QDBusMessage::createSignal("/com/nokia/icd_ui",
559                                                   "com.nokia.icd_ui",
560                                                   "retry");
561
562     QList<QVariant> arguments;
563     arguments.append(QVariant(iap));
564     arguments.append(QVariant(retry));
565     msg.setArguments(arguments);
566
567     QDBusConnection::systemBus().send(msg);
568
569     qDebug() << "Retry signal sent";
570 }
571
572 void CallListener::timerEvent(QTimerEvent* event)
573 {
574     Q_UNUSED(event);
575     killTimer(timer_);
576     timer_ = 0;
577 }
578
579 void CallListener::sleep(int ms)
580 {
581     if(timer_)
582     {
583         killTimer(timer_);
584     }
585
586     timer_ = startTimer(ms);
587
588     while(timer_)
589     {
590         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
591     }
592 }