Added call log and German translation.
[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 <QtCore/QDateTime>
22 #include "calllistener.h"
23 #include "settings.h"
24 #include "cache.h"
25 #include "contactmanager.h"
26 #include "connectionmanager.h"
27 #include "sourcecoreconfig.h"
28 #include "db.h"
29
30 namespace
31 {
32     const QString CALL_SERVICE_NAME = "com.nokia.csd";
33     const QString CALL_SERVICE_PATH = "/com/nokia/csd/call";
34     const QString CALL_SERVICE_INTERFACE = "com.nokia.csd.Call";
35     const QString CALL_SERVICE_INSTANCE_NAME = "com.nokia.csd.Call.Instance";
36     const QString CALL_SIGNAL_INCOMING = "Coming";
37     const QString CALL_SIGNAL_RELEASE = "Release";
38     const QString CALL_SIGNAL_TERMINATED = "Terminated";
39     const QString CALL_SIGNAL_ANSWERED = "AudioConnect";
40 }
41
42 QDBusConnection CallListener::systemBus_ = QDBusConnection::systemBus();
43
44 CallListener::CallListener(): source_(0),
45 closeConnection_(false), initialized_(false), box_(0), label_(0),
46 retries_(-1), timer_(0), currentCall_(0)
47 {
48 }
49
50 CallListener::~CallListener()
51 {
52     end();
53     DB::removeDatabase();
54 }
55
56 bool CallListener::begin()
57 {
58     if(Settings::instance()->getConnectionType() == Settings::ALWAYS_ASK)
59     {
60         qDebug() << "Bad connection settings, unable to start";
61         return false;
62     }
63
64     sourceId_ = Source::stringToId(Settings::instance()->get("source"));
65     QMap<QString, QString> tmpConfig;
66     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
67     config->getConfig(tmpConfig);
68     sourceConfig_ = tmpConfig;
69     delete config;
70
71     systemBus_.connect(CALL_SERVICE_NAME,
72                        CALL_SERVICE_PATH,
73                        CALL_SERVICE_INTERFACE,
74                        CALL_SIGNAL_INCOMING,
75                        this,
76                        SLOT(incomingCall(QDBusObjectPath, QString)));
77
78     systemBus_.connect(CALL_SERVICE_NAME,
79                        CALL_SERVICE_PATH,
80                        CALL_SERVICE_INTERFACE,
81                        CALL_SIGNAL_RELEASE,
82                        this,
83                        SLOT(callTerminate()));
84
85     qDebug() << "Starting...";
86
87     return true;
88
89 }
90
91 void CallListener::end()
92 {
93     systemBus_.disconnect(CALL_SERVICE_NAME,
94                           CALL_SERVICE_PATH,
95                           CALL_SERVICE_INTERFACE,
96                           CALL_SIGNAL_INCOMING,
97                           this,
98                           SLOT(incomingCall(QDBusObjectPath, QString)));
99
100     systemBus_.disconnect(CALL_SERVICE_NAME,
101                           CALL_SERVICE_PATH,
102                           CALL_SERVICE_INTERFACE,
103                           CALL_SIGNAL_RELEASE,
104                           this,
105                           SLOT(callTerminate()));
106
107     searchClose();
108     sourceConfig_.clear();
109
110 }
111
112 void CallListener::search(Source::SearchDetails const& details)
113 {
114     qDebug() << "Search called";
115
116     if(currentCall_)
117     {
118         delete currentCall_;
119     }
120
121     currentCall_ = new CallDetails;
122     currentCall_->number = details.query;
123     currentCall_->time = QDateTime::currentDateTime().toTime_t();
124     currentCall_->answered = false;
125
126     searchInit();
127
128     Source::Result result;
129
130     if(Cache::instance().findItem(details.query, result))
131     {
132
133         qDebug() << "Found from cache";
134
135         showDelayedResult(createResult(result.name,
136                                        result.street,
137                                        result.city), BANNER_DELAY);
138
139         currentCall_->result = result;
140     }
141     else
142     {
143         retries_ = 0;
144
145         if(!handleConnection())
146         {
147             qDebug() << "Unable to connect";
148             return;
149         }
150
151         showDelayedResult(tr("Searching..."), BANNER_DELAY);
152
153         qDebug() << "Starting to search...";
154
155         source_->search(details);
156     }
157
158 }
159
160 void CallListener::requestFinished(QVector <Source::Result> const& results,
161                                    Source::SearchDetails const& details,
162                                    bool error)
163 {
164     if(closeConnection_)
165     {
166         closeConnection_ = false;
167         ConnectionManager cm;
168         cm.disconnect(true);
169     }
170
171     // If box is not visible, the call must have been terminated already
172     if(!initialized_ || !box_->isVisible() || !currentCall_)
173     {
174         return;
175     }
176
177     QString message;
178
179     if(error)
180     {
181         qDebug() << "Error: " << source_->errorString();
182
183         if(retries_ < SEARCH_RETRIES && retries_ >= 0)
184         {
185             retries_++;
186             source_->search(Source::SearchDetails(currentCall_->number, "", Source::BOTH));
187             return;
188         }
189         else
190         {
191             timedMessage_ = "";
192             QString errorString;
193             Source::Error error = source_->error();
194
195             switch(error)
196             {
197             case Source::TIMEOUT:
198                 errorString = tr("Request timed out");
199                 break;
200             default:
201                 errorString = source_->errorString();
202                 break;
203             }
204
205             showError(tr("Searching failed:") + " " + errorString + ".");
206         }
207     }
208     else
209     {
210         timedMessage_ = "";
211
212         if(results.size() == 0)
213         {
214             message = tr("Phone number was not found");
215             showResult(message);
216         }
217         else
218         {
219             message = createResult(results.at(0).name, results.at(0).street,
220                                    results.at(0).city);
221             showResult(message);
222             Source::Result result = results.at(0);
223             result.number = details.query;
224             Cache::instance().addItem(result);
225             currentCall_->result = results.at(0);
226         }
227     }
228
229     retries_ = -1;
230 }
231
232 QString CallListener::createResult(QString const& name, QString const& street, QString const& city)
233 {
234     QString result = "<b>" + name + "</b>";
235
236     if(!street.isEmpty() || !city.isEmpty())
237     {
238         result += "<br>";
239
240         if(!street.isEmpty())
241         {
242             result += street + ", ";
243         }
244
245         result += city;
246     }
247
248     return result;
249 }
250
251 void CallListener::showResult(QString const& text)
252 {
253     if(!initialized_)
254     {
255         return;
256     }
257
258     label_->setText("<font color='black'>" + text + "</font>");
259
260     if(box_->isVisible())
261     {
262         box_->hide();
263     }
264
265     box_->show();
266 }
267
268 void CallListener::incomingCall(QDBusObjectPath path, QString number)
269 {
270     if(number.isEmpty())
271     {
272         qDebug() << "Unknown caller without number";
273         return;
274     }
275
276     ContactManager cm;
277
278     if(!cm.numberExists(number))
279     {
280         qDebug() << "Number doesn't exist: " << number;
281
282         systemBus_.connect(CALL_SERVICE_NAME,
283                            path.path(),
284                            CALL_SERVICE_INSTANCE_NAME,
285                            CALL_SIGNAL_TERMINATED,
286                            this,
287                            SLOT(callTerminate()));
288
289         systemBus_.connect(CALL_SERVICE_NAME,
290                            path.path(),
291                            CALL_SERVICE_INSTANCE_NAME,
292                            CALL_SIGNAL_ANSWERED,
293                            this,
294                            SLOT(handleAnswer()));
295
296         search(Source::SearchDetails(number, "", Source::BOTH));
297     }
298     else
299     {
300         qDebug() << "Number exists: " << number;
301     }
302 }
303
304 void CallListener::callTerminate()
305 {
306     if(box_ && box_->isVisible())
307     {
308         box_->hide();
309     }
310
311     if(currentCall_)
312     {
313         currentCall_->result.number = currentCall_->number;
314         Cache::instance().logItem(currentCall_->result, !currentCall_->answered, currentCall_->time);
315         delete currentCall_;
316         currentCall_ = 0;
317     }
318
319     searchClose();
320 }
321
322 void CallListener::handleAnswer()
323 {
324     qDebug() << "Answered";
325
326     if(currentCall_)
327     {
328         currentCall_->answered = true;
329     }
330 }
331
332 void CallListener::showDelayedResult(QString const& text, int delay)
333 {
334     timedMessage_ = text;
335     QTimer::singleShot(delay, this, SLOT(showTimedMessage()));
336 }
337
338 void CallListener::showTimedMessage()
339 {
340     if(timedMessage_.size() == 0 || !initialized_)
341     {
342         return;
343     }
344
345     showResult(timedMessage_);
346
347     timedMessage_ = "";
348 }
349
350 void CallListener::searchInit()
351 {
352     qDebug() << "Initializing search...";
353
354     if(initialized_)
355     {
356         qDebug() << "Already initialized";
357         return;
358     }
359
360     source_ = Source::getSource(sourceId_);
361     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
362     config->loadFromConfig(sourceConfig_);
363     config->apply(source_);
364     delete config;
365     source_->setMaxResults(1);
366     source_->setFindNumber(false);
367     source_->setTimeout(REQUEST_TIMEOUT);
368
369     connect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
370                                            Source::SearchDetails const&, bool)),
371                                            this, SLOT(requestFinished(QVector <Source::Result> const&,
372                                                                       Source::SearchDetails const&, bool)));
373     box_ = new InformationBox;
374     label_ = new QLabel("", box_);
375     label_->setMargin(8);
376     label_->setWordWrap(true);
377     box_->setWidget(label_);
378     initialized_ = true;
379 }
380
381 void CallListener::searchClose()
382 {
383     if(!initialized_)
384     {
385         return;
386     }
387
388     initialized_ = false;
389
390     qDebug() << "Closing search...";
391
392     if(source_)
393     {
394         disconnect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
395                                                   Source::SearchDetails const&, bool)),
396                                                   this, SLOT(requestFinished(QVector <Source::Result> const&,
397                                                                              Source::SearchDetails const&, bool)));
398     }
399
400     delete source_;
401     source_ = 0;
402     delete box_;
403     box_ = 0;
404     label_ = 0;
405
406     if(closeConnection_)
407     {
408         closeConnection_ = false;
409         ConnectionManager cm;
410         cm.disconnect(true);
411     }
412 }
413
414 bool CallListener::handleConnection()
415 {
416     if(!initialized_)
417     {
418         return false;
419     }
420
421     ConnectionManager cm;
422
423     if(cm.isConnected())
424     {
425         closeConnection_ = false;
426         return true;
427     }
428
429     closeConnection_ = true;
430
431     Settings::ConnectionType configType = Settings::instance()->getConnectionType();
432
433     if(configType == Settings::ALWAYS_ASK)
434     {
435         showError(tr("Automatic connecting is not allowed by settings."), BANNER_DELAY);
436         return false;
437     }
438
439     showDelayedResult(tr("Connecting..."), BANNER_DELAY);
440
441     ConnectionManager::Connection best;
442
443     ConnectionManager::ConnectionType lookupType = ConnectionManager::NO_TYPE;
444
445     switch(configType)
446     {
447     case Settings::WLAN:
448         lookupType = ConnectionManager::WLAN;
449         break;
450     case Settings::GPRS:
451         lookupType = ConnectionManager::GPRS;
452         break;
453     default:
454         lookupType = ConnectionManager::NO_TYPE;
455         break;
456     }
457
458     int cretries = 0;
459     int scans = 0;
460     bool found = false;
461     int maxScans = GPRS_SCANS;
462
463     if(lookupType != ConnectionManager::GPRS)
464     {
465         maxScans = WLAN_SCANS;
466     }
467
468     while(cretries < CONNECTION_LOOKUP_RETRIES)
469     {
470         if(!initialized_)
471         {
472             return false;
473         }
474
475         if(scans < maxScans)
476         {
477             if(cm.getBestConnection(best, lookupType))
478             {
479                 found = true;
480             }
481
482             scans++;
483         }
484
485         // If there is only gprs connection available,
486         // make sure that we are on 3g network
487         if(found && (best.type != ConnectionManager::GPRS || is3g()))
488         {
489             break;
490         }
491
492         if(found)
493         {
494             sleep(WAIT_BETWEEN_RETRIES);
495         }
496
497         qDebug() << "No connections found, retrying...";
498
499         cretries++;
500
501     }
502
503     if(cretries >= CONNECTION_LOOKUP_RETRIES)
504     {
505         showError(tr("No available 3G or WLAN networks found."));
506         return false;
507     }
508
509     int retries = 0;
510
511     while(retries < CONNECT_RETRIES)
512     {
513         if(!initialized_)
514         {
515             return false;
516         }
517
518         qDebug() << "Connecting to " << best.name;
519
520         if(cm.connect(best.id))
521         {
522             break;
523         }
524         else if(cm.error() == ConnectionManager::INVALID_IAP)
525         {
526             showError(tr("Selected access point doesn't exist."));
527             return false;
528         }
529
530         retries++;
531
532         qDebug() << "Unable to connect, retrying...";
533
534         if(retries < CONNECT_RETRIES)
535         {
536             sendRetrySignal(best.id, initialized_);
537         }
538
539     }
540
541     if(retries >= CONNECT_RETRIES)
542     {
543         sendRetrySignal(best.id, false);
544
545         if(initialized_)
546         {
547             showError(tr("Unable to connect to network."));
548         }
549
550         return false;
551     }
552
553     return initialized_;
554 }
555
556 void CallListener::showError(QString const& msg, int timeout)
557 {
558     qDebug() << "Error: " << msg;
559
560     if(!initialized_ || !box_)
561     {
562         return;
563     }
564
565     box_->setTimeout(ERROR_BANNER_TIMEOUT);
566
567     if(timeout)
568     {
569         showDelayedResult(msg, timeout);
570     }
571     else
572     {
573         showResult(msg);
574     }
575 }
576
577 bool CallListener::is3g()
578 {
579     QDBusMessage msg = QDBusMessage::createMethodCall("com.nokia.phone.net",
580                                                       "/com/nokia/phone/net",
581                                                       "Phone.Net",
582                                                       "get_registration_status");
583
584     QDBusMessage rep = systemBus_.call(msg);
585
586     if(rep.type() == QDBusMessage::ErrorMessage)
587     {
588         qDebug() << "Unable to get network status";
589         return false;
590     }
591
592     uint status = rep.arguments().value(6).toUInt();
593
594     if(status & 0x10 || status & 0x08)
595     {
596         return true;
597     }
598
599     return false;
600 }
601
602 void CallListener::sendRetrySignal(QString const& iap, bool retry)
603 {
604     QDBusMessage msg = QDBusMessage::createSignal("/com/nokia/icd_ui",
605                                                   "com.nokia.icd_ui",
606                                                   "retry");
607
608     QList<QVariant> arguments;
609     arguments.append(QVariant(iap));
610     arguments.append(QVariant(retry));
611     msg.setArguments(arguments);
612
613     QDBusConnection::systemBus().send(msg);
614
615     qDebug() << "Retry signal sent";
616 }
617
618 void CallListener::timerEvent(QTimerEvent* event)
619 {
620     Q_UNUSED(event);
621     killTimer(timer_);
622     timer_ = 0;
623 }
624
625 void CallListener::sleep(int ms)
626 {
627     if(timer_)
628     {
629         killTimer(timer_);
630     }
631
632     timer_ = startTimer(ms);
633
634     while(timer_)
635     {
636         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
637     }
638 }