rc: add nasty tizen work around.
[profile/ivi/lemolo.git] / dialer / 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
11 #include "log.h"
12 #include "gui.h"
13 #include "ofono.h"
14 #include "contacts.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_Number_Entry {
38         Eina_List *contacts;
39         char number[];
40 } Contact_Number_Entry;
41
42 struct _Contact_Info {
43         int id;
44         const char *first_name;
45         const char *last_name;
46         const char *full_name;
47         const char *picture;
48         Eina_Inlist *numbers;
49         Eina_Inlist *alias;
50         Eina_Inlist *on_del_cbs;
51         Contacts *contacts;
52         struct {
53                 Eina_Inlist *listeners;
54                 Eina_List *deleted;
55                 int walking;
56         } on_changed_cbs;
57 };
58
59 typedef struct _Contact_Info_On_Del_Ctx {
60         EINA_INLIST;
61         void (*cb)(void *, const Contact_Info *);
62         const void *data;
63 } Contact_Info_On_Del_Ctx;
64
65 typedef struct _Contact_Info_On_Changed_Ctx {
66         EINA_INLIST;
67         void (*cb)(void *, Contact_Info *);
68         const void *data;
69         Eina_Bool deleted;
70 } Contact_Info_On_Changed_Ctx;
71
72 static const char *phone_type_get(contact_number_h number);
73 static void _contact_info_free(Contact_Info *c_info);
74
75 static void _contact_number_add(char *number,
76                                 Contact_Info *c_info,
77                                 contact_number_h number_h);
78
79 static void _contact_number_entry_add(const char *number,
80                                         Contact_Info *c_info)
81 {
82         Contact_Number_Entry *e = eina_hash_find(c_info->contacts->numbers,
83                                                         number);
84         if (!e) {
85                 size_t numberlen = strlen(number);
86                 e = calloc(1, sizeof(Contact_Number_Entry) + numberlen + 1);
87                 EINA_SAFETY_ON_NULL_RETURN(e);
88                 memcpy(e->number, number, numberlen + 1);
89                 eina_hash_direct_add(c_info->contacts->numbers, e->number, e);
90         }
91         e->contacts = eina_list_append(e->contacts, c_info);
92 }
93
94 static void _contact_number_entry_del(const char *number,
95                                         Contact_Info *c_info)
96 {
97         Eina_Hash *numbers = c_info->contacts->numbers;
98         Contact_Number_Entry *e = eina_hash_find(numbers, number);
99         EINA_SAFETY_ON_NULL_RETURN(e);
100         e->contacts = eina_list_remove(e->contacts, c_info);
101         if (e->contacts)
102                 return;
103         eina_hash_del_by_key(numbers, e->number);
104         /* hash's free callback will free "e" for me */
105 }
106
107 static Eina_Bool _is_alias(Contact_Number *cn, const char *alias)
108 {
109         unsigned int aliaslen = strlen(alias);
110         unsigned int suffix = cn->numberlen - aliaslen;
111         if (cn->numberlen < aliaslen)
112                 return EINA_FALSE;
113
114         return memcmp(cn->number + suffix, alias, aliaslen) == 0;
115 }
116
117 static Eina_Bool _contact_number_is_equal(Contact_Number *cn,
118                                                 const char *number)
119 {
120         unsigned int numberlen = strlen(number);
121         if (cn->numberlen == numberlen &&
122                 memcmp(cn->number, number, numberlen) == 0)
123                 return EINA_TRUE;
124
125         return EINA_FALSE;
126 }
127
128 static void _alias_delete(Contact_Number *cn, Contact_Info *c_info)
129 {
130         Contact_Number *alias;
131         Eina_List *deleted_list = NULL;
132
133         EINA_INLIST_FOREACH(c_info->alias, alias) {
134                 if (_is_alias(cn, alias->number))
135                         deleted_list = eina_list_append(deleted_list, alias);
136         }
137
138         EINA_LIST_FREE(deleted_list, alias) {
139                 c_info->alias = eina_inlist_remove(c_info->alias,
140                                                         EINA_INLIST_GET(alias));
141                 _contact_number_entry_del(alias->number, c_info);
142                 free(alias);
143         }
144 }
145
146 static Eina_Bool _contact_phone_changed(Contact_Info *c_info,
147                                                 contact_h contact)
148 {
149         Contact_Number *cn;
150         Eina_Bool ret = EINA_FALSE;
151         Eina_List *deleted_list = NULL;
152
153         /* Looking for deleted phones */
154         EINA_INLIST_FOREACH(c_info->numbers, cn) {
155                 Eina_Bool deleted = EINA_TRUE;
156                 contact_number_iterator_h it;
157                 contact_number_h number_h;
158                 char *number;
159                 if (contact_get_number_iterator(contact, &it) !=
160                         CONTACTS_ERROR_NONE)
161                         continue;
162                 while (contact_number_iterator_has_next(it)) {
163                         if (contact_number_iterator_next(&it, &number_h) !=
164                                 CONTACTS_ERROR_NONE)
165                                 continue;
166                         if (contact_number_get_number(number_h, &number) !=
167                                 CONTACTS_ERROR_NONE)
168                                 continue;
169                         Eina_Bool equal = _contact_number_is_equal(cn, number);
170                         free(number);
171                         if (equal) {
172                                 deleted = EINA_FALSE;
173                                 break;
174                         }
175                 }
176                 if (deleted) {
177                         ret = EINA_TRUE;
178                         _contact_number_entry_del(cn->number, c_info);
179                         deleted_list = eina_list_append(deleted_list, cn);
180                 }
181         }
182
183         contact_number_iterator_h it;
184         if (contact_get_number_iterator(contact, &it) != CONTACTS_ERROR_NONE)
185                 return ret;
186
187         /* Looking for new phones */
188         while (contact_number_iterator_has_next(it)) {
189                 Eina_Bool added = EINA_TRUE;
190                 contact_number_h number_h;
191                 char *number;
192                 if (contact_number_iterator_next(&it, &number_h) !=
193                         CONTACTS_ERROR_NONE)
194                         continue;
195                 if (contact_number_get_number(number_h, &number) !=
196                         CONTACTS_ERROR_NONE)
197                         continue;
198                 EINA_INLIST_FOREACH(c_info->numbers, cn) {
199                         if (_contact_number_is_equal(cn, number)) {
200                                 added = EINA_FALSE;
201                                 break;
202                         }
203                 }
204                 if (added)
205                         _contact_number_add(number, c_info, number_h);
206                 free(number);
207         }
208
209         EINA_LIST_FREE(deleted_list, cn) {
210                 c_info->numbers = eina_inlist_remove(c_info->numbers,
211                                                         EINA_INLIST_GET(cn));
212                 _alias_delete(cn, c_info);
213                 free(cn);
214         }
215
216         return ret;
217 }
218
219 static void _contact_info_on_del_dispatch(Contact_Info *c)
220 {
221         Eina_Inlist *lst = c->on_del_cbs;
222         c->on_del_cbs = NULL; /* avoid contact_info_on_del_callback_del() */
223         while (lst) {
224                 Contact_Info_On_Del_Ctx *ctx = EINA_INLIST_CONTAINER_GET
225                         (lst, Contact_Info_On_Del_Ctx);
226
227                 lst = eina_inlist_remove(lst, lst);
228
229                 ctx->cb((void *)ctx->data, c);
230                 free(ctx);
231         }
232 }
233
234 static void _contact_info_on_changed_dispatch(Contact_Info *c)
235 {
236         Contact_Info_On_Changed_Ctx *ctx;
237
238         c->on_changed_cbs.walking++;
239         EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
240                 if (ctx->deleted)
241                         continue;
242                 ctx->cb((void *)ctx->data, c);
243         }
244         c->on_changed_cbs.walking--;
245
246         if (c->on_changed_cbs.walking <= 0) {
247                 EINA_LIST_FREE(c->on_changed_cbs.deleted, ctx) {
248                         c->on_changed_cbs.listeners = eina_inlist_remove(
249                                 c->on_changed_cbs.listeners,
250                                 EINA_INLIST_GET(ctx));
251                         free(ctx);
252                 }
253         }
254 }
255
256 static Eina_Bool _hash_foreach(const Eina_Hash *hash __UNUSED__,
257                                 const void *key __UNUSED__, void *data,
258                                 void *fdata)
259 {
260         Eina_List **deleted = fdata;
261         Eina_Bool disp = EINA_FALSE;
262         Contact_Info *c_info = data;
263         contact_h contact = NULL;
264         contact_name_h name_h = NULL;
265         char *f_name = NULL, *l_name = NULL, *img = NULL;
266
267         contact_get_from_db(c_info->id, &contact);
268         /* Contact no longer exists. */
269         if (!contact)
270                 goto deleted;
271         contact_get_name(contact, &name_h);
272         EINA_SAFETY_ON_NULL_GOTO(name_h, err_contact);
273
274         contact_name_get_detail(name_h, CONTACT_NAME_DETAIL_FIRST, &f_name);
275         contact_name_get_detail(name_h, CONTACT_NAME_DETAIL_LAST, &l_name);
276         contact_get_image(contact, &img);
277
278         if (eina_stringshare_replace(&c_info->first_name, f_name)) {
279                 disp = EINA_TRUE;
280                 eina_stringshare_del(c_info->full_name);
281                 c_info->full_name = NULL;
282         }
283
284         if (eina_stringshare_replace(&c_info->last_name, l_name)) {
285                 disp = EINA_TRUE;
286                 eina_stringshare_del(c_info->full_name);
287                 c_info->full_name = NULL;
288         }
289
290         disp |= eina_stringshare_replace(&c_info->picture, img);
291
292         disp |= _contact_phone_changed(c_info, contact);
293
294         if (disp)
295                 _contact_info_on_changed_dispatch(c_info);
296
297         free(img);
298         free(l_name);
299         free(f_name);
300 err_contact:
301         contact_destroy(contact);
302         return EINA_TRUE;
303
304 deleted:
305         *deleted = eina_list_append(*deleted, c_info);
306         return EINA_TRUE;
307 }
308
309 static void _contact_db_changed(void *data)
310 {
311         Contacts *contacts = data;
312         Contact_Info *c_info;
313         Eina_List *deleted = NULL;
314
315         EINA_SAFETY_ON_NULL_RETURN(contacts);
316         eina_hash_foreach(contacts->hash_ids, _hash_foreach, &deleted);
317
318         EINA_LIST_FREE(deleted, c_info) {
319                 Contact_Number *cn;
320                 /* _contact_info_free() will free the lists for me */
321                 EINA_INLIST_FOREACH(c_info->alias, cn)
322                         _contact_number_entry_del(cn->number, c_info);
323                 EINA_INLIST_FOREACH(c_info->numbers, cn)
324                         _contact_number_entry_del(cn->number, c_info);
325                 eina_hash_del_by_key(contacts->hash_ids, &c_info->id);
326         }
327 }
328
329 static void _contact_number_add(char *number,
330                                 Contact_Info *c_info,
331                                 contact_number_h number_h)
332 {
333         unsigned int numberlen = strlen(number);
334         Contact_Number *cn = malloc(sizeof(Contact_Number) + numberlen + 1);
335         EINA_SAFETY_ON_NULL_RETURN(cn);
336         memcpy(cn->number, number, numberlen);
337         cn->numberlen = numberlen;
338         cn->number[numberlen] = '\0';
339         cn->type = phone_type_get(number_h);
340         c_info->numbers = eina_inlist_append(c_info->numbers,
341                                                 EINA_INLIST_GET(cn));
342 }
343
344 bool _search_cb(contact_query_number_s *query, void *data)
345 {
346         Contact_Info **c_info = data;
347         char *n;
348         contact_h tizen_c;
349         contact_number_iterator_h it;
350         contact_number_h number_h;
351
352         *c_info = calloc(1, sizeof(Contact_Info));
353         EINA_SAFETY_ON_NULL_RETURN_VAL((*c_info), false);
354         (*c_info)->first_name = eina_stringshare_add(query->first_name);
355         (*c_info)->last_name = eina_stringshare_add(query->last_name);
356         (*c_info)->picture = eina_stringshare_add(query->contact_image_path);
357         (*c_info)->id = query->contact_db_id;
358         if (contact_get_from_db((*c_info)->id, &tizen_c) != CONTACTS_ERROR_NONE)
359                 return false;
360
361         if (contact_get_number_iterator(tizen_c, &it) !=
362                 CONTACTS_ERROR_NONE)
363                 return false;
364
365         while (contact_number_iterator_has_next(it)) {
366                 if (contact_number_iterator_next(&it, &number_h) !=
367                         CONTACTS_ERROR_NONE)
368                         continue;
369                 if (contact_number_get_number(number_h, &n) !=
370                         CONTACTS_ERROR_NONE)
371                         continue;
372                 _contact_number_add(n, (*c_info), number_h);
373                 free(n);
374         }
375         contact_destroy(tizen_c);
376         return false;
377 }
378
379 static const char *phone_type_get(contact_number_h number)
380 {
381         contact_number_type_e type_e;
382
383         if (contact_number_get_type(number, &type_e) < 0)
384                 return NULL;
385
386         switch (type_e) {
387         case CONTACT_NUMBER_TYPE_NONE:
388                 return "None";
389         case CONTACT_NUMBER_TYPE_HOME:
390                 return "Home";
391         case CONTACT_NUMBER_TYPE_WORK:
392                 return "Work";
393         case CONTACT_NUMBER_TYPE_VOICE:
394                 return "Home";
395         case CONTACT_NUMBER_TYPE_FAX:
396                 return "Fax";
397         case CONTACT_NUMBER_TYPE_MSG:
398                 return "Message";
399         case CONTACT_NUMBER_TYPE_CELL:
400                 return "Mobile";
401         case CONTACT_NUMBER_TYPE_PAGER:
402                 return "Pager";
403         case CONTACT_NUMBER_TYPE_BBS:
404                 return "Bulletin board";
405         case CONTACT_NUMBER_TYPE_MODEM:
406                 return "Modem";
407         case CONTACT_NUMBER_TYPE_CAR:
408                 return "Car phone";
409         case CONTACT_NUMBER_TYPE_ISDN:
410                 return "ISDN";
411         case CONTACT_NUMBER_TYPE_VIDEO:
412                 return "Video conference";
413         case CONTACT_NUMBER_TYPE_PCS:
414                 return "Personal communicatior";
415         case CONTACT_NUMBER_TYPE_ASSISTANT:
416                 return "Assistant telephone";
417         case CONTACT_NUMBER_TYPE_CUSTOM:
418                 return "Custom";
419         default:
420                 return "Unknown";
421         }
422 }
423
424 static const char *_alias_phone_type_match(Contact_Info *c_info,
425                                                 const char *alias)
426 {
427         Contact_Number *cn;
428         EINA_INLIST_FOREACH(c_info->numbers, cn) {
429                 if (_is_alias(cn, alias))
430                         return cn->type;
431         }
432         return "Unknown";
433 }
434
435 static Eina_Bool _alias_create(Contact_Info *c_info, const char *number)
436 {
437         unsigned int numberlen = strlen(number);
438         Contact_Number *cn = malloc(sizeof(Contact_Number) + numberlen + 1);
439         EINA_SAFETY_ON_NULL_RETURN_VAL(cn, EINA_FALSE);
440         memcpy(cn->number, number, numberlen);
441         cn->numberlen = numberlen;
442         cn->number[numberlen] = '\0';
443         cn->type = _alias_phone_type_match(c_info, number);
444         c_info->alias = eina_inlist_append(
445                 c_info->alias, EINA_INLIST_GET(cn));
446         _contact_number_entry_add(cn->number, c_info);
447         return EINA_TRUE;
448 }
449
450 Contact_Info *contact_search(Evas_Object *obj, const char *number,
451                                 const char **type)
452 {
453         Contact_Info *c_info = NULL, *found;
454         Contacts *contacts;
455         Contact_Number_Entry *entry;
456
457         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
458         EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
459         contacts = evas_object_data_get(obj, "contacts.ctx");
460         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
461
462         if (!contacts->contacts_on)
463                 return NULL;
464
465         entry = eina_hash_find(contacts->numbers, number);
466
467         if (entry) {
468                 c_info = eina_list_data_get(entry->contacts);
469                 EINA_SAFETY_ON_NULL_RETURN_VAL(c_info, NULL);
470                 goto get_type;
471         }
472
473         if (contact_query_contact_by_number(_search_cb, number, &c_info) < 0) {
474                 ERR("Could not fetch phone number: %s from DB", number);
475                 return NULL;
476         }
477
478         if (!c_info)
479                 return NULL;
480
481         /* Do we have this contact already ? */
482         found = eina_hash_find(contacts->hash_ids, &c_info->id);
483
484         if (found) {
485                 _contact_info_free(c_info);
486                 c_info = found;
487                 /* The user enterer an alias */
488                 Eina_Bool r;
489                 r = _alias_create(c_info, number);
490                 EINA_SAFETY_ON_FALSE_RETURN_VAL(r, NULL);
491         } else {
492                 /* New contact */
493                 c_info->contacts = contacts;
494                 eina_hash_add(contacts->hash_ids, &c_info->id, c_info);
495                 Contact_Number *cn;
496                 EINA_INLIST_FOREACH(c_info->numbers, cn)
497                         _contact_number_entry_add(cn->number, c_info);
498         }
499
500 get_type:
501         if (type)
502                 *type = contact_info_number_check(c_info, number);
503         return c_info;
504 }
505
506 const char *contact_info_picture_get(const Contact_Info *c)
507 {
508         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
509         return c->picture;
510 }
511
512 const char *contact_info_full_name_get(const Contact_Info *c)
513 {
514         Contact_Info *c2;
515         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
516         if (!c->full_name) {
517                 c2 = (Contact_Info *)c;
518                 c2->full_name = eina_stringshare_printf("%s %s", c->first_name,
519                                                         c->last_name);
520         }
521         return c->full_name;
522 }
523
524 const char *contact_info_first_name_get(const Contact_Info *c)
525 {
526         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
527         return c->first_name;
528 }
529
530 const char *contact_info_last_name_get(const Contact_Info *c)
531 {
532         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
533         return c->last_name;
534 }
535
536 const char *contact_info_detail_get(const Contact_Info *c,
537                                         const char *type)
538 {
539         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
540         EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL);
541         Contact_Number *cn;
542         /* Do not check in the alias list. Because here we want in fact a
543          * "working" numbers
544          */
545         EINA_INLIST_FOREACH(c->numbers, cn) {
546                 if (strcmp(cn->type, type) == 0)
547                         return cn->number;
548         }
549         return NULL;
550 }
551
552 const char *contact_info_number_check(const Contact_Info *c,
553                                         const char *number)
554 {
555         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
556         EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
557         Contact_Number *cn;
558
559         EINA_INLIST_FOREACH(c->numbers, cn) {
560                 if (_contact_number_is_equal(cn, number))
561                         return cn->type;
562         }
563         /* It could be an alias */
564         EINA_INLIST_FOREACH(c->alias, cn) {
565                 if (_contact_number_is_equal(cn, number))
566                         return cn->type;
567         }
568         return "Unknown";
569 }
570
571 Eina_Bool contact_info_picture_set(Contact_Info *c __UNUSED__,
572                                         const char *filename __UNUSED__)
573 {
574         ERR("TODO");
575         return EINA_FALSE;
576 }
577
578 Eina_Bool contact_info_first_name_set(Contact_Info *c __UNUSED__,
579                                         const char *name __UNUSED__)
580 {
581         ERR("TODO");
582         return EINA_FALSE;
583 }
584
585 Eina_Bool contact_info_last_name_set(Contact_Info *c __UNUSED__,
586                                         const char *name __UNUSED__)
587 {
588         ERR("TODO");
589         return EINA_FALSE;
590 }
591
592 Eina_Bool contact_info_detail_set(Contact_Info *c __UNUSED__,
593                                         const char *type __UNUSED__,
594                                         const char *number __UNUSED__)
595 {
596         ERR("TODO");
597         return EINA_FALSE;
598 }
599
600 void contact_info_on_changed_callback_add(Contact_Info *c,
601                                                 void (*cb)(void *data, Contact_Info *c),
602                                                 const void *data )
603 {
604         Contact_Info_On_Changed_Ctx *ctx;
605
606         EINA_SAFETY_ON_NULL_RETURN(c);
607         EINA_SAFETY_ON_NULL_RETURN(cb);
608
609         ctx = calloc(1, sizeof(Contact_Info_On_Changed_Ctx));
610         EINA_SAFETY_ON_NULL_RETURN(ctx);
611         ctx->cb = cb;
612         ctx->data = data;
613
614         c->on_changed_cbs.listeners = eina_inlist_append(
615                 c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
616 }
617
618 void contact_info_on_changed_callback_del(Contact_Info *c,
619                                                 void (*cb)(void *data, Contact_Info *c),
620                                                 const void *data)
621 {
622
623         Contact_Info_On_Changed_Ctx *ctx, *found = NULL;
624         EINA_SAFETY_ON_NULL_RETURN(c);
625         EINA_SAFETY_ON_NULL_RETURN(cb);
626
627         EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
628                 if (ctx->cb == cb && ctx->data == data) {
629                         found = ctx;
630                         break;
631                 }
632         }
633
634         if (!found)
635                 return;
636
637         if (c->on_changed_cbs.walking > 0) {
638                 found->deleted = EINA_TRUE;
639                 c->on_changed_cbs.deleted = eina_list_append(
640                         c->on_changed_cbs.deleted, found);
641                 return;
642         }
643
644         c->on_changed_cbs.listeners = eina_inlist_remove(
645                 c->on_changed_cbs.listeners, EINA_INLIST_GET(found));
646         free(found);
647 }
648
649 void contact_info_on_del_callback_add(Contact_Info *c,
650                                         void (*cb)(void *data, const Contact_Info *c),
651                                         const void *data)
652 {
653
654         Contact_Info_On_Del_Ctx *ctx;
655
656         EINA_SAFETY_ON_NULL_RETURN(c);
657         EINA_SAFETY_ON_NULL_RETURN(cb);
658
659         ctx = calloc(1, sizeof(Contact_Info_On_Del_Ctx));
660         EINA_SAFETY_ON_NULL_RETURN(ctx);
661         ctx->cb = cb;
662         ctx->data = data;
663
664         c->on_del_cbs = eina_inlist_append(c->on_del_cbs, EINA_INLIST_GET(ctx));
665 }
666
667 void contact_info_on_del_callback_del(Contact_Info *c,
668                                         void (*cb)(void *data, const Contact_Info *c),
669                                         const void *data )
670 {
671
672         Contact_Info_On_Del_Ctx *ctx, *found = NULL;
673         EINA_SAFETY_ON_NULL_RETURN(c);
674         EINA_SAFETY_ON_NULL_RETURN(cb);
675
676         EINA_INLIST_FOREACH(c->on_del_cbs, ctx) {
677                 if (ctx->cb == cb && ctx->data == data) {
678                         found = ctx;
679                         break;
680                 }
681         }
682
683         if (!found)
684                 return;
685
686         c->on_del_cbs = eina_inlist_remove(c->on_del_cbs,
687                                                 EINA_INLIST_GET(found));
688
689         free(found);
690 }
691
692 void contact_info_del(Contact_Info *c_info __UNUSED__)
693 {
694         ERR("TODO");
695 }
696
697 static void _contacts_ug_layout_create(struct ui_gadget *ug,
698                                         enum ug_mode mode __UNUSED__,
699                                         void *priv)
700 {
701         Contacts *contacts = priv;
702         EINA_SAFETY_ON_NULL_RETURN(ug);
703         elm_object_part_content_set(contacts->self, "elm.swallow.genlist",
704                                         ug_get_layout(ug));
705 }
706
707 static void _contact_info_free(Contact_Info *c_info)
708 {
709         Contact_Number *cn;
710         _contact_info_on_del_dispatch(c_info);
711         EINA_SAFETY_ON_FALSE_RETURN(c_info->on_changed_cbs.walking == 0);
712
713         if (c_info->on_changed_cbs.deleted) {
714                 ERR("contact still have changed deleted listeners: %p %s %s",
715                         c_info, c_info->first_name, c_info->last_name);
716                 eina_list_free(c_info->on_changed_cbs.deleted);
717         }
718
719         if (c_info->on_changed_cbs.listeners) {
720                 while (c_info->on_changed_cbs.listeners) {
721                         Contact_Info_On_Changed_Ctx *ctx;
722
723                         ctx = EINA_INLIST_CONTAINER_GET(
724                                 c_info->on_changed_cbs.listeners,
725                                 Contact_Info_On_Changed_Ctx);
726                         c_info->on_changed_cbs.listeners = eina_inlist_remove
727                                 (c_info->on_changed_cbs.listeners,
728                                         c_info->on_changed_cbs.listeners);
729                         free(ctx);
730                 }
731         }
732
733         while (c_info->numbers) {
734                 cn = EINA_INLIST_CONTAINER_GET(c_info->numbers,
735                                                 Contact_Number);
736                 c_info->numbers = eina_inlist_remove(c_info->numbers,
737                                                         c_info->numbers);
738                 free(cn);
739         }
740
741         while (c_info->alias) {
742                 cn = EINA_INLIST_CONTAINER_GET(c_info->alias,
743                                                 Contact_Number);
744                 c_info->alias = eina_inlist_remove(c_info->alias,
745                                                         c_info->alias);
746                 free(cn);
747         }
748
749         eina_stringshare_del(c_info->first_name);
750         eina_stringshare_del(c_info->last_name);
751         eina_stringshare_del(c_info->full_name);
752         eina_stringshare_del(c_info->picture);
753         free(c_info);
754 }
755
756 void _hash_elements_free(void *data)
757 {
758         Contact_Info *c = data;
759         _contact_info_free(c);
760 }
761
762 void _numbers_hash_elements_free(void *data)
763 {
764         Contact_Number_Entry *e = data;
765         /*Contact_Info will be deleted by hash_ids */
766         eina_list_free(e->contacts);
767         free(e);
768 }
769
770 static void _on_del(void *data, Evas *e __UNUSED__,
771                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
772 {
773         Contacts *contacts = data;
774         eina_hash_free(contacts->hash_ids);
775         eina_hash_free(contacts->numbers);
776         ug_destroy(contacts->ug_all);
777         if (contacts->reconnect)
778                 ecore_timer_del(contacts->reconnect);
779         free(contacts);
780         contacts_disconnect();
781 }
782
783 static void _create_contacts_ug(Contacts *contacts)
784 {
785         char buf[PATH_MAX];
786         struct ug_cbs cbs;
787         struct ui_gadget *ug;
788         bundle *bd;
789
790         cbs.priv = contacts;
791         cbs.layout_cb = _contacts_ug_layout_create;
792         cbs.result_cb = NULL;
793         cbs.destroy_cb = NULL;
794
795         bd = bundle_create();
796         EINA_SAFETY_ON_NULL_RETURN(bd);
797
798         snprintf(buf, sizeof(buf), "%d", CT_UG_REQUEST_DETAIL);
799         bundle_add(bd, CT_UG_BUNDLE_TYPE, buf);
800         snprintf(buf, sizeof(buf), "%d", 1);
801         bundle_add(bd, CT_UG_BUNDLE_ID, buf);
802
803         ug = ug_create(NULL, UG_CONTACTS_LIST, UG_MODE_FRAMEVIEW, bd, &cbs);
804         EINA_SAFETY_ON_NULL_GOTO(ug, err_ug);
805
806         contacts->ug_all = ug;
807         evas_object_data_set(contacts->self, "contacts.ctx", contacts);
808 err_ug:
809         bundle_free(bd);
810         bd = NULL;
811 }
812
813 static Eina_Bool _contacts_reconnect(void *data)
814 {
815         Contacts *contacts = data;
816
817         if (contacts_connect() != CONTACTS_ERROR_NONE)
818                 return ECORE_CALLBACK_RENEW;
819
820         contacts->contacts_on = EINA_TRUE;
821         contacts->reconnect = NULL;
822         contacts_add_contact_db_changed_cb(_contact_db_changed, contacts);
823         _create_contacts_ug(contacts);
824         return ECORE_CALLBACK_DONE;
825 }
826
827 Evas_Object *contacts_add(Evas_Object *parent)
828 {
829         Contacts *contacts;
830
831         contacts = calloc(1, sizeof(Contacts));
832         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
833         contacts->self = gui_layout_add(parent, "contacts_bg");
834         EINA_SAFETY_ON_NULL_GOTO(contacts->self, err_layout);
835         evas_object_event_callback_add(contacts->self, EVAS_CALLBACK_DEL,
836                                         _on_del, contacts);
837
838         if (contacts_connect() != CONTACTS_ERROR_NONE) {
839                 WRN("Could not connect to the contacts DB");
840                 contacts->contacts_on = EINA_FALSE;
841                 contacts->reconnect = ecore_timer_add(1.0, _contacts_reconnect,
842                                                         contacts);
843         } else {
844                 contacts_add_contact_db_changed_cb(_contact_db_changed,
845                                                         contacts);
846                 contacts->contacts_on = EINA_TRUE;
847                 _create_contacts_ug(contacts);
848         }
849
850         contacts->numbers = eina_hash_string_superfast_new(
851                 _numbers_hash_elements_free);
852         EINA_SAFETY_ON_NULL_GOTO(contacts->numbers, err_hash);
853
854         contacts->hash_ids = eina_hash_int32_new(_hash_elements_free);
855         EINA_SAFETY_ON_NULL_GOTO(contacts->hash_ids, err_hash_id);
856
857         return contacts->self;
858
859 err_hash_id:
860         eina_hash_free(contacts->numbers);
861 err_hash:
862         evas_object_del(contacts->self);
863         if (contacts->reconnect)
864                 ecore_timer_del(contacts->reconnect);
865 err_layout:
866         free(contacts);
867         return NULL;
868 }