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