4 #include <Elementary.h>
8 #include <contacts-ug.h>
17 typedef struct _Contacts {
19 Eina_Bool contacts_on;
20 Ecore_Timer *reconnect;
21 struct ui_gadget *ug_all;
22 Eina_Hash *numbers, *hash_ids;
24 * hash_ids is a pair (tizen_db_id, Contact_Info)
25 * So we won't create a duplicated Contact_Info when dealing
30 typedef struct _Contact_Number {
32 unsigned int numberlen;
37 typedef struct _Contact_Number_Entry {
40 } Contact_Number_Entry;
42 struct _Contact_Info {
44 const char *first_name;
45 const char *last_name;
46 const char *full_name;
50 Eina_Inlist *on_del_cbs;
53 Eina_Inlist *listeners;
59 typedef struct _Contact_Info_On_Del_Ctx {
61 void (*cb)(void *, const Contact_Info *);
63 } Contact_Info_On_Del_Ctx;
65 typedef struct _Contact_Info_On_Changed_Ctx {
67 void (*cb)(void *, Contact_Info *);
70 } Contact_Info_On_Changed_Ctx;
72 static const char *phone_type_get(contact_number_h number);
73 static void _contact_info_free(Contact_Info *c_info);
75 static void _contact_number_add(char *number,
77 contact_number_h number_h);
79 static void _contact_number_entry_add(const char *number,
82 Contact_Number_Entry *e = eina_hash_find(c_info->contacts->numbers,
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);
91 e->contacts = eina_list_append(e->contacts, c_info);
94 static void _contact_number_entry_del(const char *number,
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);
103 eina_hash_del_by_key(numbers, e->number);
104 /* hash's free callback will free "e" for me */
107 static Eina_Bool _is_alias(Contact_Number *cn, const char *alias)
109 unsigned int aliaslen = strlen(alias);
110 unsigned int suffix = cn->numberlen - aliaslen;
111 if (cn->numberlen < aliaslen)
114 return memcmp(cn->number + suffix, alias, aliaslen) == 0;
117 static Eina_Bool _contact_number_is_equal(Contact_Number *cn,
120 unsigned int numberlen = strlen(number);
121 if (cn->numberlen == numberlen &&
122 memcmp(cn->number, number, numberlen) == 0)
128 static void _alias_delete(Contact_Number *cn, Contact_Info *c_info)
130 Contact_Number *alias;
131 Eina_List *deleted_list = NULL;
133 EINA_INLIST_FOREACH(c_info->alias, alias) {
134 if (_is_alias(cn, alias->number))
135 deleted_list = eina_list_append(deleted_list, alias);
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);
146 static Eina_Bool _contact_phone_changed(Contact_Info *c_info,
150 Eina_Bool ret = EINA_FALSE;
151 Eina_List *deleted_list = NULL;
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;
159 if (contact_get_number_iterator(contact, &it) !=
162 while (contact_number_iterator_has_next(it)) {
163 if (contact_number_iterator_next(&it, &number_h) !=
166 if (contact_number_get_number(number_h, &number) !=
169 Eina_Bool equal = _contact_number_is_equal(cn, number);
172 deleted = EINA_FALSE;
178 _contact_number_entry_del(cn->number, c_info);
179 deleted_list = eina_list_append(deleted_list, cn);
183 contact_number_iterator_h it;
184 if (contact_get_number_iterator(contact, &it) != CONTACTS_ERROR_NONE)
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;
192 if (contact_number_iterator_next(&it, &number_h) !=
195 if (contact_number_get_number(number_h, &number) !=
198 EINA_INLIST_FOREACH(c_info->numbers, cn) {
199 if (_contact_number_is_equal(cn, number)) {
205 _contact_number_add(number, c_info, number_h);
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);
219 static void _contact_info_on_del_dispatch(Contact_Info *c)
221 Eina_Inlist *lst = c->on_del_cbs;
222 c->on_del_cbs = NULL; /* avoid contact_info_on_del_callback_del() */
224 Contact_Info_On_Del_Ctx *ctx = EINA_INLIST_CONTAINER_GET
225 (lst, Contact_Info_On_Del_Ctx);
227 lst = eina_inlist_remove(lst, lst);
229 ctx->cb((void *)ctx->data, c);
234 static void _contact_info_on_changed_dispatch(Contact_Info *c)
236 Contact_Info_On_Changed_Ctx *ctx;
238 c->on_changed_cbs.walking++;
239 EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
242 ctx->cb((void *)ctx->data, c);
244 c->on_changed_cbs.walking--;
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));
256 static Eina_Bool _hash_foreach(const Eina_Hash *hash __UNUSED__,
257 const void *key __UNUSED__, void *data,
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;
267 contact_get_from_db(c_info->id, &contact);
268 /* Contact no longer exists. */
271 contact_get_name(contact, &name_h);
272 EINA_SAFETY_ON_NULL_GOTO(name_h, err_contact);
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);
278 if (eina_stringshare_replace(&c_info->first_name, f_name)) {
280 eina_stringshare_del(c_info->full_name);
281 c_info->full_name = NULL;
284 if (eina_stringshare_replace(&c_info->last_name, l_name)) {
286 eina_stringshare_del(c_info->full_name);
287 c_info->full_name = NULL;
290 disp |= eina_stringshare_replace(&c_info->picture, img);
292 disp |= _contact_phone_changed(c_info, contact);
295 _contact_info_on_changed_dispatch(c_info);
301 contact_destroy(contact);
305 *deleted = eina_list_append(*deleted, c_info);
309 static void _contact_db_changed(void *data)
311 Contacts *contacts = data;
312 Contact_Info *c_info;
313 Eina_List *deleted = NULL;
315 EINA_SAFETY_ON_NULL_RETURN(contacts);
316 eina_hash_foreach(contacts->hash_ids, _hash_foreach, &deleted);
318 EINA_LIST_FREE(deleted, c_info) {
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);
329 static void _contact_number_add(char *number,
330 Contact_Info *c_info,
331 contact_number_h number_h)
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));
344 bool _search_cb(contact_query_number_s *query, void *data)
346 Contact_Info **c_info = data;
349 contact_number_iterator_h it;
350 contact_number_h number_h;
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)
361 if (contact_get_number_iterator(tizen_c, &it) !=
365 while (contact_number_iterator_has_next(it)) {
366 if (contact_number_iterator_next(&it, &number_h) !=
369 if (contact_number_get_number(number_h, &n) !=
372 _contact_number_add(n, (*c_info), number_h);
375 contact_destroy(tizen_c);
379 static const char *phone_type_get(contact_number_h number)
381 contact_number_type_e type_e;
383 if (contact_number_get_type(number, &type_e) < 0)
387 case CONTACT_NUMBER_TYPE_NONE:
389 case CONTACT_NUMBER_TYPE_HOME:
391 case CONTACT_NUMBER_TYPE_WORK:
393 case CONTACT_NUMBER_TYPE_VOICE:
395 case CONTACT_NUMBER_TYPE_FAX:
397 case CONTACT_NUMBER_TYPE_MSG:
399 case CONTACT_NUMBER_TYPE_CELL:
401 case CONTACT_NUMBER_TYPE_PAGER:
403 case CONTACT_NUMBER_TYPE_BBS:
404 return "Bulletin board";
405 case CONTACT_NUMBER_TYPE_MODEM:
407 case CONTACT_NUMBER_TYPE_CAR:
409 case CONTACT_NUMBER_TYPE_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:
424 static const char *_alias_phone_type_match(Contact_Info *c_info,
428 EINA_INLIST_FOREACH(c_info->numbers, cn) {
429 if (_is_alias(cn, alias))
435 static Eina_Bool _alias_create(Contact_Info *c_info, const char *number)
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);
450 Contact_Info *contact_search(Evas_Object *obj, const char *number,
453 Contact_Info *c_info = NULL, *found;
455 Contact_Number_Entry *entry;
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);
462 if (!contacts->contacts_on)
465 entry = eina_hash_find(contacts->numbers, number);
468 c_info = eina_list_data_get(entry->contacts);
469 EINA_SAFETY_ON_NULL_RETURN_VAL(c_info, NULL);
473 if (contact_query_contact_by_number(_search_cb, number, &c_info) < 0) {
474 ERR("Could not fetch phone number: %s from DB", number);
481 /* Do we have this contact already ? */
482 found = eina_hash_find(contacts->hash_ids, &c_info->id);
485 _contact_info_free(c_info);
487 /* The user enterer an alias */
489 r = _alias_create(c_info, number);
490 EINA_SAFETY_ON_FALSE_RETURN_VAL(r, NULL);
493 c_info->contacts = contacts;
494 eina_hash_add(contacts->hash_ids, &c_info->id, c_info);
496 EINA_INLIST_FOREACH(c_info->numbers, cn)
497 _contact_number_entry_add(cn->number, c_info);
502 *type = contact_info_number_check(c_info, number);
506 const char *contact_info_picture_get(const Contact_Info *c)
508 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
512 const char *contact_info_full_name_get(const Contact_Info *c)
515 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
517 c2 = (Contact_Info *)c;
518 c2->full_name = eina_stringshare_printf("%s %s", c->first_name,
524 const char *contact_info_first_name_get(const Contact_Info *c)
526 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
527 return c->first_name;
530 const char *contact_info_last_name_get(const Contact_Info *c)
532 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
536 const char *contact_info_detail_get(const Contact_Info *c,
539 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
540 EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL);
542 /* Do not check in the alias list. Because here we want in fact a
545 EINA_INLIST_FOREACH(c->numbers, cn) {
546 if (strcmp(cn->type, type) == 0)
552 const char *contact_info_number_check(const Contact_Info *c,
555 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
556 EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
559 EINA_INLIST_FOREACH(c->numbers, cn) {
560 if (_contact_number_is_equal(cn, number))
563 /* It could be an alias */
564 EINA_INLIST_FOREACH(c->alias, cn) {
565 if (_contact_number_is_equal(cn, number))
571 Eina_Bool contact_info_picture_set(Contact_Info *c __UNUSED__,
572 const char *filename __UNUSED__)
578 Eina_Bool contact_info_first_name_set(Contact_Info *c __UNUSED__,
579 const char *name __UNUSED__)
585 Eina_Bool contact_info_last_name_set(Contact_Info *c __UNUSED__,
586 const char *name __UNUSED__)
592 Eina_Bool contact_info_detail_set(Contact_Info *c __UNUSED__,
593 const char *type __UNUSED__,
594 const char *number __UNUSED__)
600 void contact_info_on_changed_callback_add(Contact_Info *c,
601 void (*cb)(void *data, Contact_Info *c),
604 Contact_Info_On_Changed_Ctx *ctx;
606 EINA_SAFETY_ON_NULL_RETURN(c);
607 EINA_SAFETY_ON_NULL_RETURN(cb);
609 ctx = calloc(1, sizeof(Contact_Info_On_Changed_Ctx));
610 EINA_SAFETY_ON_NULL_RETURN(ctx);
614 c->on_changed_cbs.listeners = eina_inlist_append(
615 c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
618 void contact_info_on_changed_callback_del(Contact_Info *c,
619 void (*cb)(void *data, Contact_Info *c),
623 Contact_Info_On_Changed_Ctx *ctx, *found = NULL;
624 EINA_SAFETY_ON_NULL_RETURN(c);
625 EINA_SAFETY_ON_NULL_RETURN(cb);
627 EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
628 if (ctx->cb == cb && ctx->data == data) {
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);
644 c->on_changed_cbs.listeners = eina_inlist_remove(
645 c->on_changed_cbs.listeners, EINA_INLIST_GET(found));
649 void contact_info_on_del_callback_add(Contact_Info *c,
650 void (*cb)(void *data, const Contact_Info *c),
654 Contact_Info_On_Del_Ctx *ctx;
656 EINA_SAFETY_ON_NULL_RETURN(c);
657 EINA_SAFETY_ON_NULL_RETURN(cb);
659 ctx = calloc(1, sizeof(Contact_Info_On_Del_Ctx));
660 EINA_SAFETY_ON_NULL_RETURN(ctx);
664 c->on_del_cbs = eina_inlist_append(c->on_del_cbs, EINA_INLIST_GET(ctx));
667 void contact_info_on_del_callback_del(Contact_Info *c,
668 void (*cb)(void *data, const Contact_Info *c),
672 Contact_Info_On_Del_Ctx *ctx, *found = NULL;
673 EINA_SAFETY_ON_NULL_RETURN(c);
674 EINA_SAFETY_ON_NULL_RETURN(cb);
676 EINA_INLIST_FOREACH(c->on_del_cbs, ctx) {
677 if (ctx->cb == cb && ctx->data == data) {
686 c->on_del_cbs = eina_inlist_remove(c->on_del_cbs,
687 EINA_INLIST_GET(found));
692 void contact_info_del(Contact_Info *c_info __UNUSED__)
697 static void _contacts_ug_layout_create(struct ui_gadget *ug,
698 enum ug_mode mode __UNUSED__,
701 Contacts *contacts = priv;
702 EINA_SAFETY_ON_NULL_RETURN(ug);
703 elm_object_part_content_set(contacts->self, "elm.swallow.genlist",
707 static void _contact_info_free(Contact_Info *c_info)
710 _contact_info_on_del_dispatch(c_info);
711 EINA_SAFETY_ON_FALSE_RETURN(c_info->on_changed_cbs.walking == 0);
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);
719 if (c_info->on_changed_cbs.listeners) {
720 while (c_info->on_changed_cbs.listeners) {
721 Contact_Info_On_Changed_Ctx *ctx;
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);
733 while (c_info->numbers) {
734 cn = EINA_INLIST_CONTAINER_GET(c_info->numbers,
736 c_info->numbers = eina_inlist_remove(c_info->numbers,
741 while (c_info->alias) {
742 cn = EINA_INLIST_CONTAINER_GET(c_info->alias,
744 c_info->alias = eina_inlist_remove(c_info->alias,
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);
756 void _hash_elements_free(void *data)
758 Contact_Info *c = data;
759 _contact_info_free(c);
762 void _numbers_hash_elements_free(void *data)
764 Contact_Number_Entry *e = data;
765 /*Contact_Info will be deleted by hash_ids */
766 eina_list_free(e->contacts);
770 static void _on_del(void *data, Evas *e __UNUSED__,
771 Evas_Object *obj __UNUSED__, void *event __UNUSED__)
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);
780 contacts_disconnect();
783 static void _create_contacts_ug(Contacts *contacts)
787 struct ui_gadget *ug;
791 cbs.layout_cb = _contacts_ug_layout_create;
792 cbs.result_cb = NULL;
793 cbs.destroy_cb = NULL;
795 bd = bundle_create();
796 EINA_SAFETY_ON_NULL_RETURN(bd);
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);
803 ug = ug_create(NULL, UG_CONTACTS_LIST, UG_MODE_FRAMEVIEW, bd, &cbs);
804 EINA_SAFETY_ON_NULL_GOTO(ug, err_ug);
806 contacts->ug_all = ug;
807 evas_object_data_set(contacts->self, "contacts.ctx", contacts);
813 static Eina_Bool _contacts_reconnect(void *data)
815 Contacts *contacts = data;
817 if (contacts_connect() != CONTACTS_ERROR_NONE)
818 return ECORE_CALLBACK_RENEW;
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;
827 Evas_Object *contacts_add(Evas_Object *parent)
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,
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,
844 contacts_add_contact_db_changed_cb(_contact_db_changed,
846 contacts->contacts_on = EINA_TRUE;
847 _create_contacts_ug(contacts);
850 contacts->numbers = eina_hash_string_superfast_new(
851 _numbers_hash_elements_free);
852 EINA_SAFETY_ON_NULL_GOTO(contacts->numbers, err_hash);
854 contacts->hash_ids = eina_hash_int32_new(_hash_elements_free);
855 EINA_SAFETY_ON_NULL_GOTO(contacts->hash_ids, err_hash_id);
857 return contacts->self;
860 eina_hash_free(contacts->numbers);
862 evas_object_del(contacts->self);
863 if (contacts->reconnect)
864 ecore_timer_del(contacts->reconnect);