bb2ffc60d12011db587252de1a354471b21301af
[apps/poi-yelp.git] / MainApp.cpp
1 #include <QNetworkReply>
2 #include <QNetworkRequest>
3 #include <QUrl>
4 #include <QUrlQuery>
5 #include <QNetworkProxy>
6 #include <QTreeWidget>
7 #include <iostream>
8 #include <error.h>
9 #include <json-c/json.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #define __STDC_FORMAT_MACROS
13 #include <inttypes.h>
14
15 #include "MainApp.h"
16 #include "Business.h"
17 #include "InfoPanel.h"
18 #include "ClickableLabel.h"
19 #include "Keyboard.h"
20 #include "traces.h"
21
22 #define DEFAULT_TEXT        "Select your destination with Yelp !"
23 #define URL_AUTH            "https://api.yelp.com/oauth2/token"
24 #define URL_AUTOCOMPLETE    "https://api.yelp.com/v3/autocomplete"
25 #define URL_SEARCH          "https://api.yelp.com/v3/businesses/search"
26
27 #define LEFT_OFFSET         28
28 #define FONT_SIZE_LINEDIT   20
29 #define FONT_SIZE_LIST      18
30 #define TEXT_INPUT_WIDTH    800
31 #define SEARCH_BTN_SIZE     105
32 #define SPACER              15
33 #define WIDGET_WIDTH        (SEARCH_BTN_SIZE + SPACER + TEXT_INPUT_WIDTH)
34 #define DISPLAY_WIDTH       TEXT_INPUT_WIDTH
35 #define DISPLAY_HEIGHT      480
36 #define COMPLETE_W_WITH_KB    1080
37 #define COMPLETE_H_WITH_KB    1488
38 #define RESULT_ITEM_HEIGHT  80
39 #define MARGINS             25
40 #define AGL_REFRESH_DELAY   75 /* milliseconds */
41
42 #define SCROLLBAR_STYLE \
43 "QScrollBar:vertical {" \
44 "    border: 2px solid grey;" \
45 "    background: gray;" \
46 "    width: 45px;" \
47 "}"
48
49 using namespace std;
50
51 MainApp::MainApp():QMainWindow(Q_NULLPTR, Qt::FramelessWindowHint),
52     networkManager(this),searchBtn(QIcon(tr(":/images/loupe-90.png")), tr(""), this),
53     lineEdit(this),keyboard(QRect(0, 688, COMPLETE_W_WITH_KB, 720), this),
54     mutex(QMutex::Recursive),token(""),currentSearchingText(""),currentSearchedText(""),
55     pSearchReply(NULL),pInfoPanel(NULL),pResultList(NULL),currentLatitude(0.0),currentLongitude(0.0),
56     navicoreSession(0),currentIndex(0),fontId(-1),isInfoScreen(false),
57     isInputDisplayed(false),isKeyboard(false),isAglNavi(false)
58 {
59     //this->setAttribute(Qt::WA_TranslucentBackground);
60     this->setStyleSheet("border: none;");
61
62     searchBtn.setStyleSheet("border: none; color: #FFFFFF;");
63     searchBtn.setMinimumSize(QSize(SEARCH_BTN_SIZE, SEARCH_BTN_SIZE));
64     searchBtn.setIconSize(searchBtn.size());
65     searchBtn.setGeometry(QRect(LEFT_OFFSET, 0, searchBtn.width(), searchBtn.height()));
66
67     lineEdit.setStyleSheet("border: none; color: #FFFFFF;");
68     lineEdit.setMinimumSize(QSize(TEXT_INPUT_WIDTH, SEARCH_BTN_SIZE));
69
70     lineEdit.setPlaceholderText(QString(DEFAULT_TEXT));
71     font = lineEdit.font();
72     font.setPointSize(FONT_SIZE_LINEDIT);
73     lineEdit.setFont(font);
74     lineEdit.setTextMargins(MARGINS/2, 0, 0, 0);
75     lineEdit.installEventFilter(this);
76     lineEdit.setGeometry(QRect(LEFT_OFFSET + searchBtn.width() + SPACER, 0, lineEdit.width(), lineEdit.height()));
77     lineEdit.setVisible(false);
78
79     /* We might need a Japanese font: */
80     QFile fontFile(":/fonts/DroidSansJapanese.ttf");
81     if (!fontFile.open(QIODevice::ReadOnly))
82     {
83         TRACE_ERROR("failed to open font file");
84     }
85     else
86     {
87         QByteArray fontData = fontFile.readAll();
88         fontId = QFontDatabase::addApplicationFontFromData(fontData);
89         if (fontId < 0)
90         {
91             TRACE_ERROR("QFontDatabase::addApplicationFontFromData failed");
92         }
93     }
94
95     /* Check if "AGL_NAVI" env variable is set. If yes, we must notify
96      * AGL environment when surface needs to be resized */
97     if (getenv("AGL_NAVI"))
98         isAglNavi = true;
99
100     connect(this, SIGNAL(positionGotSignal()), this, SLOT(positionGot()));
101     connect(this, SIGNAL(allRoutesGotSignal()), this, SLOT(allRoutesGot()));
102     connect(this, SIGNAL(routeCreatedSignal()), this, SLOT(routeCreated()));
103
104     this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
105     this->setStyleSheet("background-image: url(:/images/AGL_POI_Background.png);");
106     this->show();
107 }
108
109 MainApp::~MainApp()
110 {
111     mutex.lock();
112     if (fontId >= 0)
113         QFontDatabase::removeApplicationFont(fontId);
114
115     searchBtn.disconnect();
116     lineEdit.disconnect();
117     networkManager.disconnect();
118     keyboard.disconnect();
119
120     delete pSearchReply;
121     delete pInfoPanel;
122     mutex.unlock();
123 }
124
125 void MainApp::searchBtnClicked()
126 {
127     isInputDisplayed = !isInputDisplayed;
128     TRACE_DEBUG("isInputDisplayed = %d", isInputDisplayed);
129     DisplayLineEdit(isInputDisplayed);
130 }
131
132 void MainApp::DisplayLineEdit(bool display)
133 {
134     mutex.lock();
135
136     this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
137
138     if (display)
139     {
140         lineEdit.setVisible(true);
141         lineEdit.setFocus();
142     }
143     else
144     {
145         if (pResultList)
146         {
147             pResultList->removeEventFilter(this);
148             delete pResultList;
149             pResultList = NULL;
150         }
151         if (pInfoPanel)
152         {
153             delete pInfoPanel;
154             pInfoPanel = NULL;
155         }
156         lineEdit.setText(tr(""));
157         lineEdit.setVisible(false);
158     }
159     isInputDisplayed = display;
160
161     mutex.unlock();
162 }
163
164 void MainApp::UpdateAglSurfaces()
165 {
166     char cmd[1024];
167
168     TRACE_DEBUG("handle AGL demo surfaces (new surface is bigger)");
169     snprintf(cmd, 1023, "/usr/bin/LayerManagerControl set surface $SURFACE_ID_CLIENT source region 0 0 %d %d",
170         this->width(), this->height());
171     TRACE_DEBUG("%s", cmd);
172     system(cmd);
173     snprintf(cmd, 1023, "/usr/bin/LayerManagerControl set surface $SURFACE_ID_CLIENT destination region $CLIENT_X $CLIENT_Y %d %d",
174         this->width(), this->height());
175     TRACE_DEBUG("%s", cmd);
176     system(cmd);
177 }
178
179 void MainApp::DisplayResultList(bool display, bool RefreshDisplay)
180 {
181     mutex.lock();
182
183     if (display)
184     {
185         if (!pResultList)
186         {
187             pResultList = new QTreeWidget(this);
188             pResultList->setStyleSheet("border: none; color: #FFFFFF;");
189             pResultList->setRootIsDecorated(false);
190             pResultList->setEditTriggers(QTreeWidget::NoEditTriggers);
191             pResultList->setSelectionBehavior(QTreeWidget::SelectRows);
192             pResultList->setFrameStyle(QFrame::Box | QFrame::Plain);
193             pResultList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
194             //pResultList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
195             pResultList->setAttribute(Qt::WA_AcceptTouchEvents);
196             pResultList->verticalScrollBar()->setStyleSheet(SCROLLBAR_STYLE);
197             pResultList->header()->hide();
198             //font.setPointSize(FONT_SIZE_LIST);
199             //pResultList->setFont(font);
200             pResultList->installEventFilter(this);
201         }
202
203         pResultList->setGeometry(QRect(   LEFT_OFFSET+searchBtn.width()+SPACER, searchBtn.height()+SPACER,
204                                         DISPLAY_WIDTH, DISPLAY_HEIGHT));
205         if (RefreshDisplay)
206         {
207             this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
208         }
209         pResultList->setVisible(true);
210         pResultList->setFocus();
211     }
212     else
213     {
214         if (pResultList)
215         {
216             pResultList->removeEventFilter(this);
217             pResultList->deleteLater();
218             pResultList = NULL;
219         }
220
221         lineEdit.setFocus();
222
223         if (RefreshDisplay)
224         {
225             this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
226         }
227     }
228
229     mutex.unlock();
230 }
231
232 void MainApp::textChanged(const QString & text)
233 {
234     TRACE_INFO("New text is: %s", qPrintable(text));
235
236     /* do not handle text input if info panel is displayed: */
237     if (pInfoPanel) return;
238
239     mutex.lock();
240
241     delete pSearchReply;    /* cancel current search */
242     pSearchReply = NULL;
243
244     if (text.length() == 0) /* if empty text -> no search */
245     {
246         DisplayResultList(false);
247         mutex.unlock();
248         return;
249     }
250
251     /* if text is the same as previous search -> no need to search again */
252     if (text == currentSearchedText)
253     {
254         DisplayResultList(true);
255         FillResultList(Businesses, currentIndex);
256         mutex.unlock();
257         return;
258     }
259     this->currentSearchingText = text;
260
261     /* we need to know our current position */
262     std::vector<int32_t> Params;
263     Params.push_back(naviapi::NAVICORE_LONGITUDE);
264     Params.push_back(naviapi::NAVICORE_LATITUDE);
265     naviapi.getPosition(Params);
266
267     mutex.unlock();
268 }
269
270 void MainApp::textAdded(const QString & text)
271 {
272     mutex.lock();
273     lineEdit.setText(lineEdit.text() + text);
274
275     // be sure any text is visible on initial input
276     lineEdit.setVisible(true);
277
278     mutex.unlock();
279 }
280
281 void MainApp::keyPressed(int key)
282 {
283     mutex.lock();
284     if (key == '\b') /* backspace */
285     {
286         int len = lineEdit.text().length();
287         if (len > 0)
288             lineEdit.setText(lineEdit.text().remove(len-1, 1));
289     }
290     mutex.unlock();
291 }
292
293 void MainApp::itemClicked()
294 {
295     mutex.lock();
296     if (isInfoScreen)
297     {
298         DisplayInformation(true, false);
299     }
300     else
301     {
302         SetDestination();
303         DisplayLineEdit(false);
304     }
305     mutex.unlock();
306 }
307
308 void MainApp::ParseJsonBusinessList(const char* buf, std::vector<Business> & Output)
309 {
310     json_object *jobj = json_tokener_parse(buf);
311     if (!jobj)
312     {
313         TRACE_ERROR("json_tokener_parse failed");
314         cerr << "json_tokener_parse failed: " << buf << endl;
315         return;
316     }
317
318     json_object_object_foreach(jobj, key, val)
319     {
320         (void)key;
321         json_object *value;
322
323         if (json_object_get_type(val) == json_type_array)
324         {
325             TRACE_DEBUG_JSON("an array was found");
326
327             if(json_object_object_get_ex(jobj, "businesses", &value))
328             {
329                 TRACE_DEBUG_JSON("an business was found");
330
331                 int arraylen = json_object_array_length(value);
332
333                 for (int i = 0; i < arraylen; i++)
334                 {
335                     Business NewBusiness;
336
337                     json_object* medi_array_obj, *medi_array_obj_elem;
338                     medi_array_obj = json_object_array_get_idx(value, i);
339                     if (medi_array_obj)
340                     {
341                         if (json_object_object_get_ex(medi_array_obj, "rating", &medi_array_obj_elem))
342                         {
343                             NewBusiness.Rating = json_object_get_double(medi_array_obj_elem);
344                             TRACE_DEBUG_JSON("got Rating : %f", NewBusiness.Rating);
345                         }
346
347                         if (json_object_object_get_ex(medi_array_obj, "distance", &medi_array_obj_elem))
348                         {
349                             NewBusiness.Distance = json_object_get_double(medi_array_obj_elem);
350                             TRACE_DEBUG_JSON("got Distance : %f", NewBusiness.Distance);
351                         }
352
353                         if (json_object_object_get_ex(medi_array_obj, "review_count", &medi_array_obj_elem))
354                         {
355                             NewBusiness.ReviewCount = json_object_get_int(medi_array_obj_elem);
356                             TRACE_DEBUG_JSON("got ReviewCount : %u", NewBusiness.ReviewCount);
357                         }
358
359                         if (json_object_object_get_ex(medi_array_obj, "name", &medi_array_obj_elem))
360                         {
361                             NewBusiness.Name = QString(json_object_get_string(medi_array_obj_elem));
362                             TRACE_DEBUG_JSON("got Name : %s", qPrintable(NewBusiness.Name));
363                         }
364
365                         if (json_object_object_get_ex(medi_array_obj, "image_url", &medi_array_obj_elem))
366                         {
367                             NewBusiness.ImageUrl = QString(json_object_get_string(medi_array_obj_elem));
368                             TRACE_DEBUG_JSON("got ImageUrl : %s", qPrintable(NewBusiness.ImageUrl));
369                         }
370
371                         if (json_object_object_get_ex(medi_array_obj, "phone", &medi_array_obj_elem))
372                         {
373                             NewBusiness.Phone = QString(json_object_get_string(medi_array_obj_elem));
374                             TRACE_DEBUG_JSON("got Phone : %s", qPrintable(NewBusiness.Phone));
375                         }
376
377                         if (json_object_object_get_ex(medi_array_obj, "coordinates", &medi_array_obj_elem))
378                         {
379                             json_object *value2;
380
381                             TRACE_DEBUG_JSON("coordinates were found");
382
383                             if(json_object_object_get_ex(medi_array_obj_elem, "latitude", &value2))
384                             {
385                                 NewBusiness.Latitude = json_object_get_double(value2);
386                                 TRACE_DEBUG_JSON("got Latitude : %f", NewBusiness.Latitude);
387                             }
388
389                             if(json_object_object_get_ex(medi_array_obj_elem, "longitude", &value2))
390                             {
391                                 NewBusiness.Longitude = json_object_get_double(value2);
392                                 TRACE_DEBUG_JSON("got Longitude : %f", NewBusiness.Longitude);
393                             }
394                         }
395
396                         if (json_object_object_get_ex(medi_array_obj, "location", &medi_array_obj_elem))
397                         {
398                             json_object *value2;
399
400                             TRACE_DEBUG_JSON("a location was found");
401
402                             /* TODO: how do we deal with address2 and address3 ? */
403                             if(json_object_object_get_ex(medi_array_obj_elem, "address1", &value2))
404                             {
405                                 NewBusiness.Address = QString(json_object_get_string(value2));
406                                 TRACE_DEBUG_JSON("got Address : %s", qPrintable(NewBusiness.Address));
407                             }
408
409                             if(json_object_object_get_ex(medi_array_obj_elem, "city", &value2))
410                             {
411                                 NewBusiness.City = QString(json_object_get_string(value2));
412                                 TRACE_DEBUG_JSON("got City : %s", qPrintable(NewBusiness.City));
413                             }
414
415                             if(json_object_object_get_ex(medi_array_obj_elem, "state", &value2))
416                             {
417                                 NewBusiness.State = QString(json_object_get_string(value2));
418                                 TRACE_DEBUG_JSON("got State : %s", qPrintable(NewBusiness.State));
419                             }
420
421                             if(json_object_object_get_ex(medi_array_obj_elem, "zip_code", &value2))
422                             {
423                                 NewBusiness.ZipCode = QString(json_object_get_string(value2));
424                                 TRACE_DEBUG_JSON("got ZipCode : %s", qPrintable(NewBusiness.ZipCode));
425                             }
426
427                             if(json_object_object_get_ex(medi_array_obj_elem, "country", &value2))
428                             {
429                                 NewBusiness.Country = QString(json_object_get_string(value2));
430                                 TRACE_DEBUG_JSON("got Country : %s", qPrintable(NewBusiness.Country));
431                             }
432                         }
433
434                         /* TODO: parse categories */
435
436                         /* Add business in our list: */
437                         Businesses.push_back(NewBusiness);
438                     }
439                 }
440             }
441         }
442     }
443
444     json_object_put(jobj);
445 }
446
447 bool MainApp::eventFilter(QObject *obj, QEvent *ev)
448 {
449     bool ret = false;
450
451     mutex.lock();
452
453     if (obj == pResultList)
454     {
455         //TRACE_DEBUG("ev->type() = %d", (int)ev->type());
456
457         if (ev->type() == QEvent::KeyPress)
458         {
459             bool consumed = false;
460             int key = static_cast<QKeyEvent*>(ev)->key();
461             TRACE_DEBUG("key pressed (%d)", key);
462             switch (key) {
463                 case Qt::Key_Enter:
464                 case Qt::Key_Return:
465                     TRACE_DEBUG("enter or return");
466                     if (isInfoScreen)
467                     {
468                         DisplayInformation(true);
469                     }
470                     else
471                     {
472                         SetDestination();
473                         DisplayLineEdit(false);
474                     }
475                     consumed = true;
476                     break;
477
478                 case Qt::Key_Escape:
479                     TRACE_DEBUG("escape");
480                     DisplayResultList(false);
481                     consumed = true;
482                     break;
483
484                 case Qt::Key_Up:
485                 case Qt::Key_Down:
486                 case Qt::Key_Home:
487                 case Qt::Key_End:
488                 case Qt::Key_PageUp:
489                 case Qt::Key_PageDown:
490                     TRACE_DEBUG("arrows");
491                     break;
492
493                 default:
494                     TRACE_DEBUG("default");
495                     lineEdit.event(ev);
496                     break;
497             }
498
499             mutex.unlock();
500             return consumed;
501         }
502     }
503     else if (obj == &lineEdit)
504     {
505         if (pInfoPanel && ev->type() == QEvent::KeyPress)
506         {
507             switch(static_cast<QKeyEvent*>(ev)->key())
508             {
509                 case Qt::Key_Escape:
510                     TRACE_DEBUG("Escape !");
511                     DisplayInformation(false, false);
512                     DisplayResultList(true);
513                     FillResultList(Businesses, currentIndex);
514                     break;
515                 case Qt::Key_Enter:
516                 case Qt::Key_Return:
517                     TRACE_DEBUG("Go !");
518                     SetDestination(currentIndex);
519                     DisplayLineEdit(false);
520                     break;
521                 default: break;
522             }
523         }
524     }
525     else
526     {
527         ret = QMainWindow::eventFilter(obj, ev);
528     }
529     mutex.unlock();
530     return ret;
531 }
532
533 void MainApp::resizeEvent(QResizeEvent* event)
534 {
535     QMainWindow::resizeEvent(event);
536     if (isAglNavi)
537     {
538         QTimer::singleShot(AGL_REFRESH_DELAY, Qt::CoarseTimer, this, SLOT(UpdateAglSurfaces()));
539     }
540 }
541
542 void MainApp::SetDestination(int index)
543 {
544     mutex.lock();
545
546     /* if pResultList exists, take the selected index
547      * otherwise, take the index given as parameter */
548     if (pResultList)
549     {
550         QList<QTreeWidgetItem *> SelectedItems = pResultList->selectedItems();
551         if (SelectedItems.size() > 0)
552         {
553             /* select the first selected item : */
554             index = pResultList->indexOfTopLevelItem(*SelectedItems.begin());
555         }
556     }
557
558     TRACE_DEBUG("index is: %d", index);
559
560     /* retrieve the coordinates of this item : */
561     this->destinationLatitude = Businesses[index].Latitude;
562     this->destinationLongitude = Businesses[index].Longitude;
563
564     naviapi.getAllRoutes();
565
566     mutex.unlock();
567 }
568
569 void MainApp::DisplayInformation(bool display, bool RefreshDisplay)
570 {
571     mutex.lock();
572     if (display)
573     {
574         /* pResultList must exist, so that we can retrieve the selected index: */
575         if (!pResultList)
576         {
577             TRACE_ERROR("pResultList is null");
578             mutex.unlock();
579             return;
580         }
581
582         QList<QTreeWidgetItem *> SelectedItems = pResultList->selectedItems();
583         if (SelectedItems.size() <= 0)
584         {
585             TRACE_ERROR("no item is selected");
586             mutex.unlock();
587             return;
588         }
589
590         /* select the first selected item : */
591         currentIndex = pResultList->indexOfTopLevelItem(*SelectedItems.begin());
592
593         /* Resize window: */
594         DisplayResultList(false, false);
595
596         /* Display info for the selected item: */
597         QRect rect( LEFT_OFFSET+searchBtn.width()+SPACER, searchBtn.height()+SPACER,
598                     DISPLAY_WIDTH, DISPLAY_HEIGHT);
599         pInfoPanel = new InfoPanel(this, Businesses[currentIndex], rect);
600
601         if (RefreshDisplay)
602         {
603             this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
604         }
605
606         connect(pInfoPanel->getGoButton(),      SIGNAL(clicked(bool)), this, SLOT(goClicked()));
607         connect(pInfoPanel->getCancelButton(),  SIGNAL(clicked(bool)), this, SLOT(cancelClicked()));
608     }
609     else
610     {
611         if (pInfoPanel)
612         {
613             pInfoPanel->getGoButton()->disconnect();
614             pInfoPanel->getCancelButton()->disconnect();
615             delete pInfoPanel;
616             pInfoPanel = NULL;
617         }
618         lineEdit.setFocus();
619
620         if (RefreshDisplay)
621         {
622             this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
623         }
624     }
625
626     mutex.unlock();
627 }
628
629 void MainApp::networkReplySearch(QNetworkReply* reply)
630 {
631     QByteArray buf;
632
633     mutex.lock();
634
635     /* memorize the text which gave this result: */
636     currentSearchedText = lineEdit.text();
637
638     if (reply->error() == QNetworkReply::NoError)
639     {
640         // we only handle this callback if it matches the last search request:
641         if (reply != pSearchReply)
642         {
643             TRACE_INFO("this reply is already too late (or about a different network request)");
644             mutex.unlock();
645             return;
646         }
647
648         buf = reply->readAll();
649
650         if (buf.isEmpty())
651         {
652             mutex.unlock();
653             return;
654         }
655
656
657
658         currentIndex = 0;
659         Businesses.clear();
660         ParseJsonBusinessList(buf.data(), Businesses);
661         DisplayResultList(true);
662         FillResultList(Businesses);
663     }
664     else
665     {
666         fprintf(stderr,"POI: reply error network please check to poikey and system time (adjusted?)\n");
667     }
668
669     mutex.unlock();
670 }
671
672 /* pResultList must be allocated at this point ! */
673 int MainApp::FillResultList(vector<Business> & list, int focusIndex)
674 {
675     int nbElem = 0;
676
677     mutex.lock();
678
679     pResultList->setUpdatesEnabled(false);
680     pResultList->clear();
681
682     /* filling the dropdown menu: */
683     for (vector<Business>::iterator it = list.begin(); it != list.end(); it++)
684     {
685         /*  workaround to avoid entries with wrong coordinates returned by Yelp: */
686         if (IsCoordinatesConsistent(*it) == false)
687         {
688             list.erase(it--);
689             continue;
690         }
691
692         QTreeWidgetItem * item = new QTreeWidgetItem(pResultList);
693
694         ClickableLabel *label = new ClickableLabel("<b>"+(*it).Name+
695             "</b><br>"+(*it).Address+", "+(*it).City+", "+(*it).State+
696             " "+(*it).ZipCode+", "+(*it).Country, pResultList);
697         label->setTextFormat(Qt::RichText);
698         font.setPointSize(FONT_SIZE_LIST);
699         label->setFont(font);
700         label->setIndent(MARGINS);
701         label->setAttribute(Qt::WA_AcceptTouchEvents);
702         item->setSizeHint(0, QSize(TEXT_INPUT_WIDTH, RESULT_ITEM_HEIGHT));
703         pResultList->setItemWidget(item, 0, label);
704         connect(label, SIGNAL(clicked()), this, SLOT(itemClicked()));
705
706         //item->setText(0, (*it).Name);
707
708         if (nbElem == focusIndex)
709         {
710             pResultList->setCurrentItem(item);
711         }
712         nbElem++;
713     }
714
715     pResultList->setUpdatesEnabled(true);
716
717     mutex.unlock();
718     return nbElem;
719 }
720
721 /* Well... some of the POI returned by Yelp have coordinates which are
722  * completely inconsistent with the distance at which the POI is
723  * supposed to be.
724  * https://github.com/Yelp/yelp-fusion/issues/104
725  * Let's skip them for the moment: */
726 #define PI 3.14159265
727 #define EARTH_RADIUS 6371000
728 static inline double toRadians(double a) { return a * PI / 180.0; }
729 bool MainApp::IsCoordinatesConsistent(Business & business)
730 {
731     double lat1 = toRadians(currentLatitude);
732     double lon1 = toRadians(currentLongitude);
733     double lat2 = toRadians(business.Latitude);
734     double lon2 = toRadians(business.Longitude);
735     double x = (lon2 - lon1) * cos((lat1 + lat2)/2);
736     double y = lat2 - lat1;
737     double DistanceFromCoords = EARTH_RADIUS * sqrt(pow(x, 2) + pow(y, 2));
738
739     /* if calculated distance is not between +/- 10% of the announced
740      * distance -> skip this POI: */
741     if (DistanceFromCoords < business.Distance * 0.9 ||
742         DistanceFromCoords > business.Distance * 1.1)
743     {
744         TRACE_ERROR("Announced distance: %f, calculated distance: %f", business.Distance, DistanceFromCoords);
745         return false;
746     }
747
748     return true;
749 }
750 /* end of workaround */
751
752 bool MainApp::CheckNaviApi(int argc, char *argv[])
753 {
754     bool ret = naviapi.connect(argc, argv, this);
755
756     if (ret == true)
757     {
758         naviapi.getAllSessions();
759     }
760
761     return ret;
762 }
763
764 int MainApp::AuthenticatePOI(const QString & CredentialsFile)
765 {
766     char buf[512];
767     QString AppId;
768     QString AppSecret;
769     QString ProxyHostName;
770     QString PortStr;
771     QString User;
772     QString Password;
773     int portnum;
774
775     /* First, read AppId and AppSecret from credentials file: */
776     FILE* filep = fopen(qPrintable(CredentialsFile), "r");
777     if (!filep)
778     {
779         fprintf(stderr,"Failed to open credentials file \"%s\": %m", qPrintable(CredentialsFile));
780         return -1;
781     }
782
783     if (!fgets(buf, 512, filep))
784     {
785         fprintf(stderr,"Failed to read AppId from credentials file \"%s\"", qPrintable(CredentialsFile));
786         fclose(filep);
787         return -1;
788     }
789     if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
790         buf[strlen(buf)-1] = '\0';
791     AppId = QString(buf);
792
793     if (!fgets(buf, 512, filep))
794     {
795         fprintf(stderr,"Failed to read AppSecret from credentials file \"%s\"", qPrintable(CredentialsFile));
796         fclose(filep);
797         return -1;
798     }
799     if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
800         buf[strlen(buf)-1] = '\0';
801     AppSecret = QString(buf);
802
803     QNetworkProxy proxy;
804
805     //ProxyHostName
806     if (!fgets(buf, 512, filep))
807     {
808         TRACE_INFO("Failed to read ProxyHostName from credentials file \"%s\"", qPrintable(CredentialsFile));
809     }
810     else
811     {
812         if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
813             buf[strlen(buf)-1] = '\0';
814         ProxyHostName = QString(buf);
815         ProxyHostName.replace(0, 14, tr(""));
816
817         //Port
818         if (!fgets(buf, 512, filep))
819         {
820             TRACE_ERROR("Failed to read Port from credentials file \"%s\"", qPrintable(CredentialsFile));
821             fclose(filep);
822             return -1;
823         }
824         if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
825             buf[strlen(buf)-1] = '\0';
826         PortStr = QString(buf);
827         PortStr.replace(0, 5, tr(""));
828         portnum = PortStr.toInt();
829
830         //User
831         if (!fgets(buf, 512, filep))
832         {
833             TRACE_ERROR("Failed to read User from credentials file \"%s\"", qPrintable(CredentialsFile));
834             fclose(filep);
835             return -1;
836         }
837         if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
838             buf[strlen(buf)-1] = '\0';
839         User = QString(buf);
840         User.replace(0, 5, tr(""));
841
842         //Password
843         if (!fgets(buf, 512, filep))
844         {
845             TRACE_ERROR("Failed to read Password from credentials file \"%s\"", qPrintable(CredentialsFile));
846             fclose(filep);
847             return -1;
848         }
849         if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
850             buf[strlen(buf)-1] = '\0';
851         Password = QString(buf);
852         Password.replace(0, 9, tr(""));
853
854         proxy.setType(QNetworkProxy::HttpProxy);
855         proxy.setHostName(qPrintable(ProxyHostName));
856         proxy.setPort(portnum);
857         proxy.setUser(qPrintable(User));
858         proxy.setPassword(qPrintable(Password));
859         QNetworkProxy::setApplicationProxy(proxy);
860     }
861
862     fclose(filep);
863
864     TRACE_INFO("Found credentials");
865
866     /* Then, send a HTTP request to get the token and wait for answer (synchronously): */
867
868     token = AppSecret;
869     return 0;
870 }
871
872 int MainApp::StartMonitoringUserInput()
873 {
874     connect(&searchBtn, SIGNAL(clicked(bool)), this, SLOT(searchBtnClicked()));
875     connect(&lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
876     connect(&networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkReplySearch(QNetworkReply*)));
877     connect(&keyboard, SIGNAL(keyClicked(const QString &)), this, SLOT(textAdded(const QString &)));
878     connect(&keyboard, SIGNAL(specialKeyClicked(int)), this, SLOT(keyPressed(int)));
879     return 1;
880 }
881
882 void MainApp::SetWayPoints(uint32_t myRoute)
883 {
884     /* set the destination : */
885     naviapi::Waypoint destWp(this->destinationLatitude, this->destinationLongitude);
886     std::vector<naviapi::Waypoint> myWayPoints;
887     myWayPoints.push_back(destWp);
888     naviapi.setWaypoints(navicoreSession, myRoute, true, myWayPoints);
889
890     naviapi.calculateRoute(navicoreSession, myRoute);
891
892     /* reset search: */
893     currentSearchingText = tr("");
894     currentSearchedText = tr("");
895     currentIndex = 0;
896     Businesses.clear();
897 }
898
899 void MainApp::goClicked()
900 {
901     TRACE_DEBUG("Go clicked !");
902     SetDestination(currentIndex);
903     DisplayLineEdit(false);
904 }
905
906 void MainApp::cancelClicked()
907 {
908     TRACE_DEBUG("Cancel clicked !");
909     DisplayInformation(false, false);
910     DisplayResultList(true, false);
911     FillResultList(Businesses, currentIndex);
912 }
913
914 void MainApp::getAllSessions_reply(const std::map< uint32_t, std::string >& allSessions)
915 {
916     mutex.lock();
917
918     if (allSessions.empty())
919     {
920         TRACE_ERROR("Error: could not find an instance of Navicore");
921         mutex.unlock();
922         return;
923     }
924
925     this->navicoreSession = allSessions.begin()->first;
926
927     TRACE_INFO("Current session: %d", this->navicoreSession);
928
929     mutex.unlock();
930 }
931
932
933 void MainApp::getPosition_reply(std::map< int32_t, naviapi::variant > position)
934 {
935     mutex.lock();
936
937     std::map< int32_t, naviapi::variant >::iterator it;
938     for (it = position.begin(); it != position.end(); it++)
939     {
940         if (it->first == naviapi::NAVICORE_LATITUDE)
941         {
942             currentLatitude = it->second._double;
943         }
944         else if (it->first == naviapi::NAVICORE_LONGITUDE)
945         {
946             currentLongitude = it->second._double;
947         }
948     }
949
950     TRACE_INFO("Current position: %f, %f", currentLatitude, currentLongitude);
951
952     mutex.unlock();
953
954     emit positionGotSignal();
955 }
956
957 void MainApp::getAllRoutes_reply(std::vector< uint32_t > allRoutes)
958 {
959     mutex.lock();
960
961     uint32_t routeHandle = 0;
962
963     if (allRoutes.size() != 0)
964     {
965         routeHandle = allRoutes[0];
966     }
967
968     this->currentRouteHandle = routeHandle;
969
970     mutex.unlock();
971
972     emit allRoutesGotSignal();
973 }
974
975 void MainApp::createRoute_reply(uint32_t routeHandle)
976 {
977     mutex.lock();
978
979     this->currentRouteHandle = routeHandle;
980
981     mutex.unlock();
982
983     emit routeCreatedSignal();
984 }
985
986 void MainApp::positionGot()
987 {
988     mutex.lock();
989
990     /* let's generate a search request : */
991     QString myUrlStr = URL_SEARCH + tr("?") + tr("term=") + currentSearchingText +
992         tr("&latitude=") + QString::number(currentLatitude) +
993         tr("&longitude=") + QString::number(currentLongitude);
994
995     TRACE_DEBUG("URL: %s", qPrintable(myUrlStr));
996
997     QUrl myUrl = QUrl(myUrlStr);
998     QNetworkRequest req(myUrl);
999     req.setRawHeader(QByteArray("Authorization"), (tr("bearer ") + token).toLocal8Bit());
1000
1001     /* Then, send a HTTP request to get the token and wait for answer (synchronously): */
1002
1003     pSearchReply = networkManager.get(req);
1004
1005     mutex.unlock();
1006 }
1007
1008 void MainApp::allRoutesGot()
1009 {
1010     mutex.lock();
1011
1012     /* check if a route already exists, if not create it : */
1013     if (this->currentRouteHandle == 0)
1014     {
1015         naviapi.createRoute(navicoreSession);
1016     }
1017     else
1018     {
1019         naviapi.pauseSimulation(navicoreSession);
1020         naviapi.setSimulationMode(navicoreSession, false);
1021         naviapi.cancelRouteCalculation(navicoreSession, this->currentRouteHandle);
1022         sleep(1);
1023
1024         SetWayPoints(this->currentRouteHandle);
1025     }
1026
1027     mutex.unlock();
1028 }
1029
1030 void MainApp::routeCreated()
1031 {
1032     mutex.lock();
1033
1034     SetWayPoints(this->currentRouteHandle);
1035
1036     mutex.unlock();
1037 }
1038