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