Fixed compile issues with Tizen contacts
[profile/ivi/lemolo.git] / utils / contacts-tizen.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include <Elementary.h>
5 #include <Eet.h>
6 #include <Eina.h>
7 #include <ui-gadget.h>
8 #include <contacts-ug.h>
9 #include <contacts.h>
10 #include <aul.h>
11
12 #include "log.h"
13 #include "ofono.h"
14 #include "contacts-ofono-efl.h"
15 #include "util.h"
16
17 typedef struct _Contacts {
18         Evas_Object *self;
19         Eina_Bool contacts_on;
20         Ecore_Timer *reconnect;
21         struct ui_gadget *ug_all;
22         Eina_Hash *numbers, *hash_ids;
23         /*
24          * hash_ids is a pair (tizen_db_id, Contact_Info)
25          * So we won't create a duplicated Contact_Info when dealing
26          * with alias numbers
27          */
28 } Contacts;
29
30 typedef struct  _Contact_Number {
31         EINA_INLIST;
32         unsigned int numberlen;
33         const char *type;
34         char number[];
35 } Contact_Number;
36
37 typedef struct _Contact_Name_Search {
38         const char *name;
39         Contact_Info *c_info;
40 } Contact_Name_Search;
41
42 typedef struct _Contact_Number_Entry {
43         Eina_List *contacts;
44         char number[];
45 } Contact_Number_Entry;
46
47 struct _Contact_Info {
48         int id;
49         const char *first_name;
50         const char *last_name;
51         const char *full_name;
52         const char *picture;
53         Eina_Inlist *numbers;
54         Eina_Inlist *alias;
55         Eina_Inlist *on_del_cbs;
56         Contacts *contacts;
57         struct {
58                 Eina_Inlist *listeners;
59                 Eina_List *deleted;
60                 int walking;
61         } on_changed_cbs;
62 };
63
64 typedef struct _Contact_Info_On_Del_Ctx {
65         EINA_INLIST;
66         void (*cb)(void *, const Contact_Info *);
67         const void *data;
68 } Contact_Info_On_Del_Ctx;
69
70 typedef struct _Contact_Info_On_Changed_Ctx {
71         EINA_INLIST;
72         void (*cb)(void *, Contact_Info *);
73         const void *data;
74         Eina_Bool deleted;
75 } Contact_Info_On_Changed_Ctx;
76
77 struct _Contact_Partial_Match
78 {
79         const Contact_Info *info;
80         const char *type;
81         Eina_Bool name_match : 1;
82 };
83
84 typedef struct _Partial_Match_Search
85 {
86         Eina_List *matches;
87         const Contacts *contacts;
88 } Partial_Match_Search;
89
90
91 static void _contact_number_entry_add(const char *number, Contact_Info *c_info);
92
93 static const char *phone_type_get(contact_number_h number);
94 static void _contact_info_free(Contact_Info *c_info);
95
96 static void _contact_number_add(char *number,
97                                 Contact_Info *c_info,
98                                 contact_number_h number_h);
99
100 const char *contact_info_number_check(const Contact_Info *c,
101                                         const char *number);
102
103 static void _partial_match_add(Eina_List **p_list, const char *type,
104                                 const Contact_Info *c_info,
105                                 Eina_Bool name_match)
106 {
107         Contact_Partial_Match *pm = malloc(sizeof(Contact_Partial_Match));
108         EINA_SAFETY_ON_NULL_RETURN(pm);
109         pm->info = c_info;
110         pm->type = type;
111         pm->name_match = name_match;
112         *p_list = eina_list_append(*p_list, pm);
113 }
114
115 static void _partial_number_match_add(Eina_List **p_list, const char *type,
116                                         const Contact_Info *c_info)
117 {
118         _partial_match_add(p_list, type, c_info, EINA_FALSE);
119 }
120
121 static bool _number_partial_search(contact_query_number_s *query, void *data)
122 {
123         Partial_Match_Search *pm_search = data;
124         const Contacts *contacts = pm_search->contacts;
125         char *n;
126         contact_h tizen_c;
127         const char *type;
128         contact_number_iterator_h it;
129         contact_number_h number_h;
130         Contact_Info *c_info;
131         Contact_Number *cn;
132
133         c_info = eina_hash_find(contacts->hash_ids, &query->contact_db_id);
134
135         if (c_info)
136                 goto exit;
137
138         c_info = calloc(1, sizeof(Contact_Info));
139         EINA_SAFETY_ON_NULL_RETURN_VAL(c_info, true);
140         c_info->first_name = eina_stringshare_add(query->first_name);
141         c_info->last_name = eina_stringshare_add(query->last_name);
142         c_info->picture = eina_stringshare_add(query->contact_image_path);
143         c_info->id = query->contact_db_id;
144         if (contact_get_from_db(c_info->id, &tizen_c) != CONTACTS_ERROR_NONE)
145                 return true;
146
147         if (contact_get_number_iterator(tizen_c, &it) !=
148                 CONTACTS_ERROR_NONE)
149                 return true;
150
151         while (contact_number_iterator_has_next(it)) {
152                 if (contact_number_iterator_next(&it, &number_h) !=
153                         CONTACTS_ERROR_NONE)
154                         continue;
155                 if (contact_number_get_number(number_h, &n) !=
156                         CONTACTS_ERROR_NONE)
157                         continue;
158                 _contact_number_add(n, c_info, number_h);
159                 free(n);
160         }
161         contact_destroy(tizen_c);
162
163         /* New contact */
164         c_info->contacts = (Contacts *)contacts;
165         eina_hash_add(contacts->hash_ids, &c_info->id, c_info);
166         EINA_INLIST_FOREACH(c_info->numbers, cn)
167                 _contact_number_entry_add(cn->number, c_info);
168
169 exit:
170         type = contact_info_number_check(c_info, query->phone_number);
171         _partial_number_match_add(&pm_search->matches, type,
172                                         c_info);
173         return true;
174
175 }
176
177 static bool _name_partial_search(contact_query_name_s *query, void *data)
178 {
179         Partial_Match_Search *pm_search = data;
180         const Contacts *contacts = pm_search->contacts;
181         char *n;
182         contact_h tizen_c;
183         contact_number_iterator_h it;
184         contact_number_h number_h;
185         Contact_Info *c_info;
186         Contact_Number *cn;
187         Eina_Bool c_info_new = EINA_FALSE;
188
189
190         c_info = eina_hash_find(contacts->hash_ids, &query->contact_db_id);
191
192         if (c_info)
193                 goto exit;
194
195         c_info = calloc(1, sizeof(Contact_Info));
196         EINA_SAFETY_ON_NULL_RETURN_VAL(c_info, true);
197         c_info->first_name = eina_stringshare_add(query->first_name);
198         c_info->last_name = eina_stringshare_add(query->last_name);
199         c_info->picture = eina_stringshare_add(query->contact_image_path);
200         c_info->id = query->contact_db_id;
201         if (contact_get_from_db(c_info->id, &tizen_c) != CONTACTS_ERROR_NONE)
202                 return true;
203
204         if (contact_get_number_iterator(tizen_c, &it) !=
205                 CONTACTS_ERROR_NONE)
206                 return true;
207
208         while (contact_number_iterator_has_next(it)) {
209                 if (contact_number_iterator_next(&it, &number_h) !=
210                         CONTACTS_ERROR_NONE)
211                         continue;
212                 if (contact_number_get_number(number_h, &n) !=
213                         CONTACTS_ERROR_NONE)
214                         continue;
215                 _contact_number_add(n, c_info, number_h);
216                 free(n);
217         }
218         contact_destroy(tizen_c);
219
220         /* New contact */
221         c_info->contacts = (Contacts *)contacts;
222         eina_hash_add(contacts->hash_ids, &c_info->id, c_info);
223         c_info_new = EINA_TRUE;
224 exit:
225         EINA_INLIST_FOREACH(c_info->numbers, cn) {
226                 if (c_info_new)
227                         _contact_number_entry_add(cn->number, c_info);
228                 _partial_match_add(&pm_search->matches, cn->type, c_info,
229                                         EINA_TRUE);
230         }
231         return true;
232 }
233
234 Eina_List *contact_partial_match_search(Evas_Object *obj, const char *query)
235 {
236
237         const Contacts *contacts;
238         int i, j;
239         Eina_Bool name_search = EINA_FALSE;
240         char *query_number;
241         Partial_Match_Search pm_search;
242
243         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
244         EINA_SAFETY_ON_NULL_RETURN_VAL(query, NULL);
245         contacts = evas_object_data_get(obj, "contacts.ctx");
246         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
247
248         if (!contacts->contacts_on)
249                 return NULL;
250
251         /* Check if it is numeric */
252         query_number = alloca(strlen(query) + 1);
253         EINA_SAFETY_ON_NULL_RETURN_VAL(query_number, NULL);
254         for (i = 0, j = 0; query[i] != '\0'; i++) {
255                 if (isalpha(query[i])) {
256                         name_search = EINA_TRUE;
257                         break;
258                 } else if (isdigit(query[i]))
259                         query_number[j++] = query[i];
260         }
261
262         pm_search.contacts = contacts;
263         pm_search.matches = NULL;
264
265         if (name_search) {
266                 if (contact_query_contact_by_name(_name_partial_search, query,
267                                                         &pm_search) < 0) {
268                         ERR("Could not search in contacts DB the name: %s",
269                                 query);
270                         return NULL;
271                 }
272         } else {
273                 query_number[j] = '\0';
274                 if (contact_query_contact_by_number(_number_partial_search,
275                                                         query,
276                                                         &pm_search) < 0) {
277                         ERR("Could not search in contacts DB the number: %s",
278                                 query);
279                         return NULL;
280                 }
281         }
282
283         return pm_search.matches;
284 }
285
286 void contact_partial_match_search_free(Eina_List *results)
287 {
288         Contact_Partial_Match *pm;
289         EINA_LIST_FREE(results, pm)
290                 free(pm);
291 }
292
293 const char *contact_partial_match_type_get(const Contact_Partial_Match *pm)
294 {
295         EINA_SAFETY_ON_NULL_RETURN_VAL(pm, NULL);
296         return pm->type;
297 }
298
299 const Contact_Info *contact_partial_match_info_get(const Contact_Partial_Match *pm)
300 {
301         EINA_SAFETY_ON_NULL_RETURN_VAL(pm, NULL);
302         return pm->info;
303 }
304
305 Eina_Bool contact_partial_match_name_match_get(const Contact_Partial_Match *pm)
306 {
307         EINA_SAFETY_ON_NULL_RETURN_VAL(pm, EINA_FALSE);
308         return pm->name_match;
309 }
310
311 Eina_List *contact_info_all_numbers_get(const Contact_Info *c)
312 {
313         Eina_List *l = NULL;
314         Contact_Number *cn;
315
316         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
317
318         EINA_INLIST_FOREACH(c->numbers, cn)
319                 l = eina_list_append(l, cn->number);
320
321         return l;
322 }
323
324 static void _contact_number_entry_add(const char *number,
325                                         Contact_Info *c_info)
326 {
327         Contact_Number_Entry *e = eina_hash_find(c_info->contacts->numbers,
328                                                         number);
329         if (!e) {
330                 size_t numberlen = strlen(number);
331                 e = calloc(1, sizeof(Contact_Number_Entry) + numberlen + 1);
332                 EINA_SAFETY_ON_NULL_RETURN(e);
333                 memcpy(e->number, number, numberlen + 1);
334                 eina_hash_direct_add(c_info->contacts->numbers, e->number, e);
335         }
336         e->contacts = eina_list_append(e->contacts, c_info);
337 }
338
339 static void _contact_number_entry_del(const char *number,
340                                         Contact_Info *c_info)
341 {
342         Eina_Hash *numbers = c_info->contacts->numbers;
343         Contact_Number_Entry *e = eina_hash_find(numbers, number);
344         EINA_SAFETY_ON_NULL_RETURN(e);
345         e->contacts = eina_list_remove(e->contacts, c_info);
346         if (e->contacts)
347                 return;
348         eina_hash_del_by_key(numbers, e->number);
349         /* hash's free callback will free "e" for me */
350 }
351
352 static Eina_Bool _is_alias(Contact_Number *cn, const char *alias)
353 {
354         unsigned int aliaslen = strlen(alias);
355         unsigned int suffix = cn->numberlen - aliaslen;
356         if (cn->numberlen < aliaslen)
357                 return EINA_FALSE;
358
359         return memcmp(cn->number + suffix, alias, aliaslen) == 0;
360 }
361
362 static Eina_Bool _contact_number_is_equal(Contact_Number *cn,
363                                                 const char *number)
364 {
365         unsigned int numberlen = strlen(number);
366         if (cn->numberlen == numberlen &&
367                 memcmp(cn->number, number, numberlen) == 0)
368                 return EINA_TRUE;
369
370         return EINA_FALSE;
371 }
372
373 static void _alias_delete(Contact_Number *cn, Contact_Info *c_info)
374 {
375         Contact_Number *alias;
376         Eina_List *deleted_list = NULL;
377
378         EINA_INLIST_FOREACH(c_info->alias, alias) {
379                 if (_is_alias(cn, alias->number))
380                         deleted_list = eina_list_append(deleted_list, alias);
381         }
382
383         EINA_LIST_FREE(deleted_list, alias) {
384                 c_info->alias = eina_inlist_remove(c_info->alias,
385                                                         EINA_INLIST_GET(alias));
386                 _contact_number_entry_del(alias->number, c_info);
387                 free(alias);
388         }
389 }
390
391 static Eina_Bool _contact_phone_changed(Contact_Info *c_info,
392                                                 contact_h contact)
393 {
394         Contact_Number *cn;
395         Eina_Bool ret = EINA_FALSE;
396         Eina_List *deleted_list = NULL;
397
398         /* Looking for deleted phones */
399         EINA_INLIST_FOREACH(c_info->numbers, cn) {
400                 Eina_Bool deleted = EINA_TRUE;
401                 contact_number_iterator_h it;
402                 contact_number_h number_h;
403                 char *number;
404                 if (contact_get_number_iterator(contact, &it) !=
405                         CONTACTS_ERROR_NONE)
406                         continue;
407                 while (contact_number_iterator_has_next(it)) {
408                         if (contact_number_iterator_next(&it, &number_h) !=
409                                 CONTACTS_ERROR_NONE)
410                                 continue;
411                         if (contact_number_get_number(number_h, &number) !=
412                                 CONTACTS_ERROR_NONE)
413                                 continue;
414                         Eina_Bool equal = _contact_number_is_equal(cn, number);
415                         free(number);
416                         if (equal) {
417                                 deleted = EINA_FALSE;
418                                 break;
419                         }
420                 }
421                 if (deleted) {
422                         ret = EINA_TRUE;
423                         _contact_number_entry_del(cn->number, c_info);
424                         deleted_list = eina_list_append(deleted_list, cn);
425                 }
426         }
427
428         contact_number_iterator_h it;
429         if (contact_get_number_iterator(contact, &it) != CONTACTS_ERROR_NONE)
430                 return ret;
431
432         /* Looking for new phones */
433         while (contact_number_iterator_has_next(it)) {
434                 Eina_Bool added = EINA_TRUE;
435                 contact_number_h number_h;
436                 char *number;
437                 if (contact_number_iterator_next(&it, &number_h) !=
438                         CONTACTS_ERROR_NONE)
439                         continue;
440                 if (contact_number_get_number(number_h, &number) !=
441                         CONTACTS_ERROR_NONE)
442                         continue;
443                 EINA_INLIST_FOREACH(c_info->numbers, cn) {
444                         if (_contact_number_is_equal(cn, number)) {
445                                 added = EINA_FALSE;
446                                 break;
447                         }
448                 }
449                 if (added)
450                         _contact_number_add(number, c_info, number_h);
451                 free(number);
452         }
453
454         EINA_LIST_FREE(deleted_list, cn) {
455                 c_info->numbers = eina_inlist_remove(c_info->numbers,
456                                                         EINA_INLIST_GET(cn));
457                 _alias_delete(cn, c_info);
458                 free(cn);
459         }
460
461         return ret;
462 }
463
464 static void _contact_info_on_del_dispatch(Contact_Info *c)
465 {
466         Eina_Inlist *lst = c->on_del_cbs;
467         c->on_del_cbs = NULL; /* avoid contact_info_on_del_callback_del() */
468         while (lst) {
469                 Contact_Info_On_Del_Ctx *ctx = EINA_INLIST_CONTAINER_GET
470                         (lst, Contact_Info_On_Del_Ctx);
471
472                 lst = eina_inlist_remove(lst, lst);
473
474                 ctx->cb((void *)ctx->data, c);
475                 free(ctx);
476         }
477 }
478
479 static void _contact_info_on_changed_dispatch(Contact_Info *c)
480 {
481         Contact_Info_On_Changed_Ctx *ctx;
482
483         c->on_changed_cbs.walking++;
484         EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
485                 if (ctx->deleted)
486                         continue;
487                 ctx->cb((void *)ctx->data, c);
488         }
489         c->on_changed_cbs.walking--;
490
491         if (c->on_changed_cbs.walking <= 0) {
492                 EINA_LIST_FREE(c->on_changed_cbs.deleted, ctx) {
493                         c->on_changed_cbs.listeners = eina_inlist_remove(
494                                 c->on_changed_cbs.listeners,
495                                 EINA_INLIST_GET(ctx));
496                         free(ctx);
497                 }
498         }
499 }
500
501 static Eina_Bool _hash_foreach(const Eina_Hash *hash __UNUSED__,
502                                 const void *key __UNUSED__, void *data,
503                                 void *fdata)
504 {
505         Eina_List **deleted = fdata;
506         Eina_Bool disp = EINA_FALSE;
507         Contact_Info *c_info = data;
508         contact_h contact = NULL;
509         contact_name_h name_h = NULL;
510         char *f_name = NULL, *l_name = NULL, *img = NULL;
511
512         contact_get_from_db(c_info->id, &contact);
513         /* Contact no longer exists. */
514         if (!contact)
515                 goto deleted;
516         contact_get_name(contact, &name_h);
517         EINA_SAFETY_ON_NULL_GOTO(name_h, err_contact);
518
519         contact_name_get_detail(name_h, CONTACT_NAME_DETAIL_FIRST, &f_name);
520         contact_name_get_detail(name_h, CONTACT_NAME_DETAIL_LAST, &l_name);
521         contact_get_image(contact, &img);
522
523         if (eina_stringshare_replace(&c_info->first_name, f_name)) {
524                 disp = EINA_TRUE;
525                 eina_stringshare_del(c_info->full_name);
526                 c_info->full_name = NULL;
527         }
528
529         if (eina_stringshare_replace(&c_info->last_name, l_name)) {
530                 disp = EINA_TRUE;
531                 eina_stringshare_del(c_info->full_name);
532                 c_info->full_name = NULL;
533         }
534
535         disp |= eina_stringshare_replace(&c_info->picture, img);
536
537         disp |= _contact_phone_changed(c_info, contact);
538
539         if (disp)
540                 _contact_info_on_changed_dispatch(c_info);
541
542         free(img);
543         free(l_name);
544         free(f_name);
545 err_contact:
546         contact_destroy(contact);
547         return EINA_TRUE;
548
549 deleted:
550         *deleted = eina_list_append(*deleted, c_info);
551         return EINA_TRUE;
552 }
553
554 static void _contact_db_changed(void *data)
555 {
556         Contacts *contacts = data;
557         Contact_Info *c_info;
558         Eina_List *deleted = NULL;
559
560         EINA_SAFETY_ON_NULL_RETURN(contacts);
561         eina_hash_foreach(contacts->hash_ids, _hash_foreach, &deleted);
562
563         EINA_LIST_FREE(deleted, c_info) {
564                 Contact_Number *cn;
565                 /* _contact_info_free() will free the lists for me */
566                 EINA_INLIST_FOREACH(c_info->alias, cn)
567                         _contact_number_entry_del(cn->number, c_info);
568                 EINA_INLIST_FOREACH(c_info->numbers, cn)
569                         _contact_number_entry_del(cn->number, c_info);
570                 eina_hash_del_by_key(contacts->hash_ids, &c_info->id);
571         }
572 }
573
574 static void _contact_number_add(char *number,
575                                 Contact_Info *c_info,
576                                 contact_number_h number_h)
577 {
578         unsigned int numberlen = strlen(number);
579         Contact_Number *cn = malloc(sizeof(Contact_Number) + numberlen + 1);
580         EINA_SAFETY_ON_NULL_RETURN(cn);
581         memcpy(cn->number, number, numberlen);
582         cn->numberlen = numberlen;
583         cn->number[numberlen] = '\0';
584         cn->type = phone_type_get(number_h);
585         c_info->numbers = eina_inlist_append(c_info->numbers,
586                                                 EINA_INLIST_GET(cn));
587 }
588
589 bool _search_cb(contact_query_number_s *query, void *data)
590 {
591         Contact_Info **c_info = data;
592         char *n;
593         contact_h tizen_c;
594         contact_number_iterator_h it;
595         contact_number_h number_h;
596
597         *c_info = calloc(1, sizeof(Contact_Info));
598         EINA_SAFETY_ON_NULL_RETURN_VAL((*c_info), false);
599         (*c_info)->first_name = eina_stringshare_add(query->first_name);
600         (*c_info)->last_name = eina_stringshare_add(query->last_name);
601         (*c_info)->picture = eina_stringshare_add(query->contact_image_path);
602         (*c_info)->id = query->contact_db_id;
603         if (contact_get_from_db((*c_info)->id, &tizen_c) != CONTACTS_ERROR_NONE)
604                 return false;
605
606         if (contact_get_number_iterator(tizen_c, &it) !=
607                 CONTACTS_ERROR_NONE)
608                 return false;
609
610         while (contact_number_iterator_has_next(it)) {
611                 if (contact_number_iterator_next(&it, &number_h) !=
612                         CONTACTS_ERROR_NONE)
613                         continue;
614                 if (contact_number_get_number(number_h, &n) !=
615                         CONTACTS_ERROR_NONE)
616                         continue;
617                 _contact_number_add(n, (*c_info), number_h);
618                 free(n);
619         }
620         contact_destroy(tizen_c);
621         return false;
622 }
623
624 static const char *phone_type_get(contact_number_h number)
625 {
626         contact_number_type_e type_e;
627
628         if (contact_number_get_type(number, &type_e) < 0)
629                 return NULL;
630
631         switch (type_e) {
632         case CONTACT_NUMBER_TYPE_NONE:
633                 return "None";
634         case CONTACT_NUMBER_TYPE_HOME:
635                 return "Home";
636         case CONTACT_NUMBER_TYPE_WORK:
637                 return "Work";
638         case CONTACT_NUMBER_TYPE_VOICE:
639                 return "Home";
640         case CONTACT_NUMBER_TYPE_FAX:
641                 return "Fax";
642         case CONTACT_NUMBER_TYPE_MSG:
643                 return "Message";
644         case CONTACT_NUMBER_TYPE_CELL:
645                 return "Mobile";
646         case CONTACT_NUMBER_TYPE_PAGER:
647                 return "Pager";
648         case CONTACT_NUMBER_TYPE_BBS:
649                 return "Bulletin board";
650         case CONTACT_NUMBER_TYPE_MODEM:
651                 return "Modem";
652         case CONTACT_NUMBER_TYPE_CAR:
653                 return "Car phone";
654         case CONTACT_NUMBER_TYPE_ISDN:
655                 return "ISDN";
656         case CONTACT_NUMBER_TYPE_VIDEO:
657                 return "Video conference";
658         case CONTACT_NUMBER_TYPE_PCS:
659                 return "Personal communicatior";
660         case CONTACT_NUMBER_TYPE_ASSISTANT:
661                 return "Assistant telephone";
662         case CONTACT_NUMBER_TYPE_CUSTOM:
663                 return "Custom";
664         default:
665                 return "Unknown";
666         }
667 }
668
669 static const char *_alias_phone_type_match(Contact_Info *c_info,
670                                                 const char *alias)
671 {
672         Contact_Number *cn;
673         EINA_INLIST_FOREACH(c_info->numbers, cn) {
674                 if (_is_alias(cn, alias))
675                         return cn->type;
676         }
677         return "Unknown";
678 }
679
680 static Eina_Bool _alias_create(Contact_Info *c_info, const char *number)
681 {
682         unsigned int numberlen = strlen(number);
683         Contact_Number *cn = malloc(sizeof(Contact_Number) + numberlen + 1);
684         EINA_SAFETY_ON_NULL_RETURN_VAL(cn, EINA_FALSE);
685         memcpy(cn->number, number, numberlen);
686         cn->numberlen = numberlen;
687         cn->number[numberlen] = '\0';
688         cn->type = _alias_phone_type_match(c_info, number);
689         c_info->alias = eina_inlist_append(
690                 c_info->alias, EINA_INLIST_GET(cn));
691         _contact_number_entry_add(cn->number, c_info);
692         return EINA_TRUE;
693 }
694
695 Contact_Info *contact_search(Evas_Object *obj, const char *number,
696                                 const char **type)
697 {
698         Contact_Info *c_info = NULL, *found;
699         Contacts *contacts;
700         Contact_Number_Entry *entry;
701
702         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
703         EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
704         contacts = evas_object_data_get(obj, "contacts.ctx");
705         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
706
707         if (!contacts->contacts_on)
708                 return NULL;
709
710         entry = eina_hash_find(contacts->numbers, number);
711
712         if (entry) {
713                 c_info = eina_list_data_get(entry->contacts);
714                 EINA_SAFETY_ON_NULL_RETURN_VAL(c_info, NULL);
715                 goto get_type;
716         }
717
718         if (contact_query_contact_by_number(_search_cb, number, &c_info) < 0) {
719                 ERR("Could not fetch phone number: %s from DB", number);
720                 return NULL;
721         }
722
723         if (!c_info)
724                 return NULL;
725
726         /* Do we have this contact already ? */
727         found = eina_hash_find(contacts->hash_ids, &c_info->id);
728
729         if (found) {
730                 _contact_info_free(c_info);
731                 c_info = found;
732                 /* The user enterer an alias */
733                 Eina_Bool r;
734                 r = _alias_create(c_info, number);
735                 EINA_SAFETY_ON_FALSE_RETURN_VAL(r, NULL);
736         } else {
737                 /* New contact */
738                 c_info->contacts = contacts;
739                 eina_hash_add(contacts->hash_ids, &c_info->id, c_info);
740                 Contact_Number *cn;
741                 EINA_INLIST_FOREACH(c_info->numbers, cn)
742                         _contact_number_entry_add(cn->number, c_info);
743         }
744
745 get_type:
746         if (type)
747                 *type = contact_info_number_check(c_info, number);
748         return c_info;
749 }
750
751 const char *contact_info_picture_get(const Contact_Info *c)
752 {
753         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
754         return c->picture;
755 }
756
757 const char *contact_info_full_name_get(const Contact_Info *c)
758 {
759         Contact_Info *c2;
760         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
761         if (!c->full_name) {
762                 c2 = (Contact_Info *)c;
763                 c2->full_name = eina_stringshare_printf("%s %s", c->first_name,
764                                                         c->last_name);
765         }
766         return c->full_name;
767 }
768
769 const char *contact_info_first_name_get(const Contact_Info *c)
770 {
771         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
772         return c->first_name;
773 }
774
775 const char *contact_info_last_name_get(const Contact_Info *c)
776 {
777         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
778         return c->last_name;
779 }
780
781 const char *contact_info_detail_get(const Contact_Info *c,
782                                         const char *type)
783 {
784         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
785         EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL);
786         Contact_Number *cn;
787         /* Do not check in the alias list. Because here we want in fact a
788          * "working" numbers
789          */
790         EINA_INLIST_FOREACH(c->numbers, cn) {
791                 if (strcmp(cn->type, type) == 0)
792                         return cn->number;
793         }
794         return NULL;
795 }
796
797 const char *contact_info_number_check(const Contact_Info *c,
798                                         const char *number)
799 {
800         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
801         EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
802         Contact_Number *cn;
803
804         EINA_INLIST_FOREACH(c->numbers, cn) {
805                 if (_contact_number_is_equal(cn, number))
806                         return cn->type;
807         }
808         /* It could be an alias */
809         EINA_INLIST_FOREACH(c->alias, cn) {
810                 if (_contact_number_is_equal(cn, number))
811                         return cn->type;
812         }
813         return "Unknown";
814 }
815
816 Eina_Bool contact_info_picture_set(Contact_Info *c __UNUSED__,
817                                         const char *filename __UNUSED__)
818 {
819         ERR("TODO");
820         return EINA_FALSE;
821 }
822
823 Eina_Bool contact_info_first_name_set(Contact_Info *c __UNUSED__,
824                                         const char *name __UNUSED__)
825 {
826         ERR("TODO");
827         return EINA_FALSE;
828 }
829
830 Eina_Bool contact_info_last_name_set(Contact_Info *c __UNUSED__,
831                                         const char *name __UNUSED__)
832 {
833         ERR("TODO");
834         return EINA_FALSE;
835 }
836
837 Eina_Bool contact_info_detail_set(Contact_Info *c __UNUSED__,
838                                         const char *type __UNUSED__,
839                                         const char *number __UNUSED__)
840 {
841         ERR("TODO");
842         return EINA_FALSE;
843 }
844
845 void contact_info_on_changed_callback_add(Contact_Info *c,
846                                                 void (*cb)(void *data, Contact_Info *c),
847                                                 const void *data )
848 {
849         Contact_Info_On_Changed_Ctx *ctx;
850
851         EINA_SAFETY_ON_NULL_RETURN(c);
852         EINA_SAFETY_ON_NULL_RETURN(cb);
853
854         ctx = calloc(1, sizeof(Contact_Info_On_Changed_Ctx));
855         EINA_SAFETY_ON_NULL_RETURN(ctx);
856         ctx->cb = cb;
857         ctx->data = data;
858
859         c->on_changed_cbs.listeners = eina_inlist_append(
860                 c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
861 }
862
863 void contact_info_on_changed_callback_del(Contact_Info *c,
864                                                 void (*cb)(void *data, Contact_Info *c),
865                                                 const void *data)
866 {
867
868         Contact_Info_On_Changed_Ctx *ctx, *found = NULL;
869         EINA_SAFETY_ON_NULL_RETURN(c);
870         EINA_SAFETY_ON_NULL_RETURN(cb);
871
872         EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
873                 if (ctx->cb == cb && ctx->data == data) {
874                         found = ctx;
875                         break;
876                 }
877         }
878
879         if (!found)
880                 return;
881
882         if (c->on_changed_cbs.walking > 0) {
883                 found->deleted = EINA_TRUE;
884                 c->on_changed_cbs.deleted = eina_list_append(
885                         c->on_changed_cbs.deleted, found);
886                 return;
887         }
888
889         c->on_changed_cbs.listeners = eina_inlist_remove(
890                 c->on_changed_cbs.listeners, EINA_INLIST_GET(found));
891         free(found);
892 }
893
894 void contact_info_on_del_callback_add(Contact_Info *c,
895                                         void (*cb)(void *data, const Contact_Info *c),
896                                         const void *data)
897 {
898
899         Contact_Info_On_Del_Ctx *ctx;
900
901         EINA_SAFETY_ON_NULL_RETURN(c);
902         EINA_SAFETY_ON_NULL_RETURN(cb);
903
904         ctx = calloc(1, sizeof(Contact_Info_On_Del_Ctx));
905         EINA_SAFETY_ON_NULL_RETURN(ctx);
906         ctx->cb = cb;
907         ctx->data = data;
908
909         c->on_del_cbs = eina_inlist_append(c->on_del_cbs, EINA_INLIST_GET(ctx));
910 }
911
912 void contact_info_on_del_callback_del(Contact_Info *c,
913                                         void (*cb)(void *data, const Contact_Info *c),
914                                         const void *data )
915 {
916
917         Contact_Info_On_Del_Ctx *ctx, *found = NULL;
918         EINA_SAFETY_ON_NULL_RETURN(c);
919         EINA_SAFETY_ON_NULL_RETURN(cb);
920
921         EINA_INLIST_FOREACH(c->on_del_cbs, ctx) {
922                 if (ctx->cb == cb && ctx->data == data) {
923                         found = ctx;
924                         break;
925                 }
926         }
927
928         if (!found)
929                 return;
930
931         c->on_del_cbs = eina_inlist_remove(c->on_del_cbs,
932                                                 EINA_INLIST_GET(found));
933
934         free(found);
935 }
936
937 void contact_info_del(Contact_Info *c_info __UNUSED__)
938 {
939         ERR("TODO");
940 }
941
942 static void _contacts_ug_layout_create(struct ui_gadget *ug,
943                                         enum ug_mode mode __UNUSED__,
944                                         void *priv)
945 {
946         Contacts *contacts = priv;
947         EINA_SAFETY_ON_NULL_RETURN(ug);
948         elm_object_part_content_set(contacts->self, "elm.swallow.genlist",
949                                         ug_get_layout(ug));
950 }
951
952 static void _contact_info_free(Contact_Info *c_info)
953 {
954         Contact_Number *cn;
955         _contact_info_on_del_dispatch(c_info);
956         EINA_SAFETY_ON_FALSE_RETURN(c_info->on_changed_cbs.walking == 0);
957
958         if (c_info->on_changed_cbs.deleted) {
959                 ERR("contact still have changed deleted listeners: %p %s %s",
960                         c_info, c_info->first_name, c_info->last_name);
961                 eina_list_free(c_info->on_changed_cbs.deleted);
962         }
963
964         if (c_info->on_changed_cbs.listeners) {
965                 while (c_info->on_changed_cbs.listeners) {
966                         Contact_Info_On_Changed_Ctx *ctx;
967
968                         ctx = EINA_INLIST_CONTAINER_GET(
969                                 c_info->on_changed_cbs.listeners,
970                                 Contact_Info_On_Changed_Ctx);
971                         c_info->on_changed_cbs.listeners = eina_inlist_remove
972                                 (c_info->on_changed_cbs.listeners,
973                                         c_info->on_changed_cbs.listeners);
974                         free(ctx);
975                 }
976         }
977
978         while (c_info->numbers) {
979                 cn = EINA_INLIST_CONTAINER_GET(c_info->numbers,
980                                                 Contact_Number);
981                 c_info->numbers = eina_inlist_remove(c_info->numbers,
982                                                         c_info->numbers);
983                 free(cn);
984         }
985
986         while (c_info->alias) {
987                 cn = EINA_INLIST_CONTAINER_GET(c_info->alias,
988                                                 Contact_Number);
989                 c_info->alias = eina_inlist_remove(c_info->alias,
990                                                         c_info->alias);
991                 free(cn);
992         }
993
994         eina_stringshare_del(c_info->first_name);
995         eina_stringshare_del(c_info->last_name);
996         eina_stringshare_del(c_info->full_name);
997         eina_stringshare_del(c_info->picture);
998         free(c_info);
999 }
1000
1001 void _hash_elements_free(void *data)
1002 {
1003         Contact_Info *c = data;
1004         _contact_info_free(c);
1005 }
1006
1007 void _numbers_hash_elements_free(void *data)
1008 {
1009         Contact_Number_Entry *e = data;
1010         /*Contact_Info will be deleted by hash_ids */
1011         eina_list_free(e->contacts);
1012         free(e);
1013 }
1014
1015 static void _on_del(void *data, Evas *e __UNUSED__,
1016                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
1017 {
1018         Contacts *contacts = data;
1019         eina_hash_free(contacts->hash_ids);
1020         eina_hash_free(contacts->numbers);
1021         ug_destroy(contacts->ug_all);
1022         if (contacts->reconnect)
1023                 ecore_timer_del(contacts->reconnect);
1024         free(contacts);
1025         contacts_disconnect();
1026 }
1027
1028 static void _create_contacts_ug(Contacts *contacts)
1029 {
1030         char buf[PATH_MAX];
1031         struct ug_cbs cbs;
1032         struct ui_gadget *ug;
1033         bundle *bd;
1034
1035         cbs.priv = contacts;
1036         cbs.layout_cb = _contacts_ug_layout_create;
1037         cbs.result_cb = NULL;
1038         cbs.destroy_cb = NULL;
1039
1040         bd = bundle_create();
1041         EINA_SAFETY_ON_NULL_RETURN(bd);
1042
1043         snprintf(buf, sizeof(buf), "%d", CT_UG_REQUEST_DETAIL);
1044         bundle_add(bd, CT_UG_BUNDLE_TYPE, buf);
1045         snprintf(buf, sizeof(buf), "%d", 1);
1046         bundle_add(bd, CT_UG_BUNDLE_ID, buf);
1047
1048         ug = ug_create(NULL, UG_CONTACTS_LIST, UG_MODE_FRAMEVIEW, bd, &cbs);
1049         EINA_SAFETY_ON_NULL_GOTO(ug, err_ug);
1050
1051         contacts->ug_all = ug;
1052         evas_object_data_set(contacts->self, "contacts.ctx", contacts);
1053 err_ug:
1054         bundle_free(bd);
1055         bd = NULL;
1056 }
1057
1058 static Eina_Bool _contacts_reconnect(void *data)
1059 {
1060         Contacts *contacts = data;
1061
1062         if (contacts_connect() != CONTACTS_ERROR_NONE)
1063                 return ECORE_CALLBACK_RENEW;
1064
1065         contacts->contacts_on = EINA_TRUE;
1066         contacts->reconnect = NULL;
1067         contacts_add_contact_db_changed_cb(_contact_db_changed, contacts);
1068         _create_contacts_ug(contacts);
1069         return ECORE_CALLBACK_DONE;
1070 }
1071
1072 Evas_Object *contacts_add(Evas_Object *parent)
1073 {
1074         Contacts *contacts;
1075
1076         contacts = calloc(1, sizeof(Contacts));
1077         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
1078         contacts->self = layout_add(parent, "contacts_bg");
1079         EINA_SAFETY_ON_NULL_GOTO(contacts->self, err_layout);
1080         evas_object_event_callback_add(contacts->self, EVAS_CALLBACK_DEL,
1081                                         _on_del, contacts);
1082
1083         if (contacts_connect() != CONTACTS_ERROR_NONE) {
1084                 WRN("Could not connect to the contacts DB");
1085                 contacts->contacts_on = EINA_FALSE;
1086                 contacts->reconnect = ecore_timer_add(1.0, _contacts_reconnect,
1087                                                         contacts);
1088         } else {
1089                 contacts_add_contact_db_changed_cb(_contact_db_changed,
1090                                                         contacts);
1091                 contacts->contacts_on = EINA_TRUE;
1092                 _create_contacts_ug(contacts);
1093         }
1094
1095         contacts->numbers = eina_hash_string_superfast_new(
1096                 _numbers_hash_elements_free);
1097         EINA_SAFETY_ON_NULL_GOTO(contacts->numbers, err_hash);
1098
1099         contacts->hash_ids = eina_hash_int32_new(_hash_elements_free);
1100         EINA_SAFETY_ON_NULL_GOTO(contacts->hash_ids, err_hash_id);
1101
1102         return contacts->self;
1103
1104 err_hash_id:
1105         eina_hash_free(contacts->numbers);
1106 err_hash:
1107         evas_object_del(contacts->self);
1108         if (contacts->reconnect)
1109                 ecore_timer_del(contacts->reconnect);
1110 err_layout:
1111         free(contacts);
1112         return NULL;
1113 }