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