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