Integration with tizen contacts
authorIscaro <iscaro@profusion.mobi>
Mon, 13 Aug 2012 19:52:59 +0000 (16:52 -0300)
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Wed, 29 Aug 2012 23:47:54 +0000 (20:47 -0300)
Makefile.am
configure.ac
dialer/contacts-tizen.c [new file with mode: 0644]
dialer/gui.c

index 4316c7e..fb037e9 100644 (file)
@@ -4,7 +4,8 @@ AM_CFLAGS = \
        -DPACKAGE_DATA_DIR=\"$(pkgdatadir)/\" \
        -DPACKAGE_BIN_DIR=\"$(bindir)\" \
        -DPACKAGE_LIB_DIR=\"$(libdir)\" \
-       @EFL_CFLAGS@
+       @EFL_CFLAGS@ \
+       @TIZEN_CFLAGS@
 
 MAINTAINERCLEANFILES = \
        aclocal.m4 \
@@ -22,7 +23,7 @@ MAINTAINERCLEANFILES = \
 
 bin_PROGRAMS = dialer/dialer
 
-dialer_dialer_LDADD = @EFL_LIBS@
+dialer_dialer_LDADD = @EFL_LIBS@ @TIZEN_LIBS@
 dialer_dialer_SOURCES = \
        dialer/main.c \
        dialer/log.h \
@@ -34,7 +35,6 @@ dialer_dialer_SOURCES = \
        dialer/gui.h \
        dialer/keypad.c \
        dialer/keypad.h \
-       dialer/contacts.c \
        dialer/contacts.h \
        dialer/history.c \
        dialer/history.h \
@@ -45,6 +45,12 @@ dialer_dialer_SOURCES = \
        dialer/util.c \
        dialer/util.h
 
+if HAVE_TIZEN
+dialer_dialer_SOURCES += dialer/contacts-tizen.c
+else
+dialer_dialer_SOURCES += dialer/contacts.c
+endif
+
 # Themes are compiled with edje_cc given by user (cross-compile)
 EDJE_CC = @edje_cc@
 EDJE_FLAGS_VERBOSE_ =
index 0625278..1ea820a 100644 (file)
@@ -34,6 +34,34 @@ PKG_CHECK_MODULES([EFL],
 
 EFL_WITH_BIN([edje], [edje-cc], [edje_cc])
 
+want_tizen="auto"
+have_tizen="no"
+AC_ARG_ENABLE([tizen],
+   AC_HELP_STRING([--enable-tizen], [enable Tizen integration. @<:@default==auto@:>@]),
+   [want_tizen="yes"],
+   [want_tizen="no"])
+
+if test "$want_tizen" != "no"; then
+   PKG_CHECK_MODULES([TIZEN],
+                [
+                        ecore-x
+                        appcore-efl
+                        ui-gadget
+                        capi-social-contacts
+                ],
+                [have_tizen="yes"],
+                [have_tizen="no"])
+
+fi
+
+if test "$want_tizen" = "yes" -a "$have_tizen" = "no"; then
+   AC_MSG_ERROR([Want tizen, but not found])
+elif test "$have_tizen" = "yes"; then
+     AC_DEFINE([HAVE_TIZEN], 1, [Tizen support])
+fi
+
+AM_CONDITIONAL([HAVE_TIZEN], [test "$have_tizen" = "yes"])
+
 with_max_log_level="EINA_LOG_LEVEL_DBG"
 AC_ARG_WITH(maximum-log-level,
    [AC_HELP_STRING([--with-maximum-log-level=NUMBER],
diff --git a/dialer/contacts-tizen.c b/dialer/contacts-tizen.c
new file mode 100644 (file)
index 0000000..c24c684
--- /dev/null
@@ -0,0 +1,841 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <Elementary.h>
+#include <Eet.h>
+#include <Eina.h>
+#include <ui-gadget.h>
+#include <contacts-ug.h>
+#include <contacts.h>
+
+#include "log.h"
+#include "gui.h"
+#include "ofono.h"
+#include "contacts.h"
+#include "util.h"
+
+typedef struct _Contacts {
+       Evas_Object *self;
+       struct ui_gadget *ug_all;
+       Eina_Hash *numbers, *hash_ids;
+       /*
+        * hash_ids is a pair (tizen_db_id, Contact_Info)
+        * So we won't create a duplicated Contact_Info when dealing
+        * with alias numbers
+        */
+} Contacts;
+
+typedef struct  _Contact_Number {
+       EINA_INLIST;
+       unsigned int numberlen;
+       const char *type;
+       char number[];
+} Contact_Number;
+
+typedef struct _Contact_Number_Entry {
+       Eina_List *contacts;
+       char number[];
+} Contact_Number_Entry;
+
+struct _Contact_Info {
+       int id;
+       const char *first_name;
+       const char *last_name;
+       const char *full_name;
+       const char *picture;
+       Eina_Inlist *numbers;
+       Eina_Inlist *alias;
+       Eina_Inlist *on_del_cbs;
+       Contacts *contacts;
+       struct {
+               Eina_Inlist *listeners;
+               Eina_List *deleted;
+               int walking;
+       } on_changed_cbs;
+};
+
+typedef struct _Contact_Info_On_Del_Ctx {
+       EINA_INLIST;
+       void (*cb)(void *, const Contact_Info *);
+       const void *data;
+} Contact_Info_On_Del_Ctx;
+
+typedef struct _Contact_Info_On_Changed_Ctx {
+       EINA_INLIST;
+       void (*cb)(void *, Contact_Info *);
+       const void *data;
+       Eina_Bool deleted;
+} Contact_Info_On_Changed_Ctx;
+
+static const char *phone_type_get(contact_number_h number);
+static void _contact_info_free(Contact_Info *c_info);
+
+static void _contact_number_add(char *number,
+                               Contact_Info *c_info,
+                               contact_number_h number_h);
+
+static void _contact_number_entry_add(const char *number,
+                                       Contact_Info *c_info)
+{
+       Contact_Number_Entry *e = eina_hash_find(c_info->contacts->numbers,
+                                                       number);
+       if (!e) {
+               size_t numberlen = strlen(number);
+               e = calloc(1, sizeof(Contact_Number_Entry) + numberlen + 1);
+               EINA_SAFETY_ON_NULL_RETURN(e);
+               memcpy(e->number, number, numberlen + 1);
+               eina_hash_direct_add(c_info->contacts->numbers, e->number, e);
+       }
+       e->contacts = eina_list_append(e->contacts, c_info);
+}
+
+static void _contact_number_entry_del(const char *number,
+                                       Contact_Info *c_info)
+{
+       Eina_Hash *numbers = c_info->contacts->numbers;
+       Contact_Number_Entry *e = eina_hash_find(numbers, number);
+       EINA_SAFETY_ON_NULL_RETURN(e);
+       e->contacts = eina_list_remove(e->contacts, c_info);
+       if (e->contacts)
+               return;
+       eina_hash_del_by_key(numbers, e->number);
+       /* hash's free callback will free "e" for me */
+}
+
+static Eina_Bool _is_alias(Contact_Number *cn, const char *alias)
+{
+       unsigned int aliaslen = strlen(alias);
+       unsigned int suffix = cn->numberlen - aliaslen;
+       if (cn->numberlen < aliaslen)
+               return EINA_FALSE;
+
+       return memcmp(cn->number + suffix, alias, aliaslen) == 0;
+}
+
+static Eina_Bool _contact_number_is_equal(Contact_Number *cn,
+                                               const char *number)
+{
+       unsigned int numberlen = strlen(number);
+       if (cn->numberlen == numberlen &&
+               memcmp(cn->number, number, numberlen) == 0)
+               return EINA_TRUE;
+
+       return EINA_FALSE;
+}
+
+static void _alias_delete(Contact_Number *cn, Contact_Info *c_info)
+{
+       Contact_Number *alias;
+       Eina_List *deleted_list = NULL;
+
+       EINA_INLIST_FOREACH(c_info->alias, alias) {
+               if (_is_alias(cn, alias->number))
+                       deleted_list = eina_list_append(deleted_list, alias);
+       }
+
+       EINA_LIST_FREE(deleted_list, alias) {
+               c_info->alias = eina_inlist_remove(c_info->alias,
+                                                       EINA_INLIST_GET(alias));
+               _contact_number_entry_del(alias->number, c_info);
+               free(alias);
+       }
+}
+
+static Eina_Bool _contact_phone_changed(Contact_Info *c_info,
+                                               contact_h contact)
+{
+       Contact_Number *cn;
+       Eina_Bool ret = EINA_FALSE;
+       Eina_List *deleted_list = NULL;
+
+       /* Looking for deleted phones */
+       EINA_INLIST_FOREACH(c_info->numbers, cn) {
+               Eina_Bool deleted = EINA_TRUE;
+               contact_number_iterator_h it;
+               contact_number_h number_h;
+               char *number;
+               if (contact_get_number_iterator(contact, &it) !=
+                       CONTACTS_ERROR_NONE)
+                       continue;
+               while (contact_number_iterator_has_next(it)) {
+                       if (contact_number_iterator_next(&it, &number_h) !=
+                               CONTACTS_ERROR_NONE)
+                               continue;
+                       if (contact_number_get_number(number_h, &number) !=
+                               CONTACTS_ERROR_NONE)
+                               continue;
+                       Eina_Bool equal = _contact_number_is_equal(cn, number);
+                       free(number);
+                       if (equal) {
+                               deleted = EINA_FALSE;
+                               break;
+                       }
+               }
+               if (deleted) {
+                       ret = EINA_TRUE;
+                       _contact_number_entry_del(cn->number, c_info);
+                       deleted_list = eina_list_append(deleted_list, cn);
+               }
+       }
+
+       contact_number_iterator_h it;
+       if (contact_get_number_iterator(contact, &it) != CONTACTS_ERROR_NONE)
+               return ret;
+
+       /* Looking for new phones */
+       while (contact_number_iterator_has_next(it)) {
+               Eina_Bool added = EINA_TRUE;
+               contact_number_h number_h;
+               char *number;
+               if (contact_number_iterator_next(&it, &number_h) !=
+                       CONTACTS_ERROR_NONE)
+                       continue;
+               if (contact_number_get_number(number_h, &number) !=
+                       CONTACTS_ERROR_NONE)
+                       continue;
+               EINA_INLIST_FOREACH(c_info->numbers, cn) {
+                       if (_contact_number_is_equal(cn, number)) {
+                               added = EINA_FALSE;
+                               break;
+                       }
+               }
+               if (added)
+                       _contact_number_add(number, c_info, number_h);
+               free(number);
+       }
+
+       EINA_LIST_FREE(deleted_list, cn) {
+               c_info->numbers = eina_inlist_remove(c_info->numbers,
+                                                       EINA_INLIST_GET(cn));
+               _alias_delete(cn, c_info);
+               free(cn);
+       }
+
+       return ret;
+}
+
+static void _contact_info_on_del_dispatch(Contact_Info *c)
+{
+       Eina_Inlist *lst = c->on_del_cbs;
+       c->on_del_cbs = NULL; /* avoid contact_info_on_del_callback_del() */
+       while (lst) {
+               Contact_Info_On_Del_Ctx *ctx = EINA_INLIST_CONTAINER_GET
+                       (lst, Contact_Info_On_Del_Ctx);
+
+               lst = eina_inlist_remove(lst, lst);
+
+               ctx->cb((void *)ctx->data, c);
+               free(ctx);
+       }
+}
+
+static void _contact_info_on_changed_dispatch(Contact_Info *c)
+{
+       Contact_Info_On_Changed_Ctx *ctx;
+
+       c->on_changed_cbs.walking++;
+       EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
+               if (ctx->deleted)
+                       continue;
+               ctx->cb((void *)ctx->data, c);
+       }
+       c->on_changed_cbs.walking--;
+
+       if (c->on_changed_cbs.walking <= 0) {
+               EINA_LIST_FREE(c->on_changed_cbs.deleted, ctx) {
+                       c->on_changed_cbs.listeners = eina_inlist_remove(
+                               c->on_changed_cbs.listeners,
+                               EINA_INLIST_GET(ctx));
+                       free(ctx);
+               }
+       }
+}
+
+static Eina_Bool _hash_foreach(const Eina_Hash *hash __UNUSED__,
+                               const void *key __UNUSED__, void *data,
+                               void *fdata)
+{
+       Eina_List **deleted = fdata;
+       Eina_Bool disp = EINA_FALSE;
+       Contact_Info *c_info = data;
+       contact_h contact = NULL;
+       contact_name_h name_h = NULL;
+       char *f_name = NULL, *l_name = NULL, *img = NULL;
+
+       contact_get_from_db(c_info->id, &contact);
+       /* Contact no longer exists. */
+       if (!contact)
+               goto deleted;
+       contact_get_name(contact, &name_h);
+       EINA_SAFETY_ON_NULL_GOTO(name_h, err_contact);
+
+       contact_name_get_detail(name_h, CONTACT_NAME_DETAIL_FIRST, &f_name);
+       contact_name_get_detail(name_h, CONTACT_NAME_DETAIL_LAST, &l_name);
+       contact_get_image(contact, &img);
+
+       if (eina_stringshare_replace(&c_info->first_name, f_name)) {
+               disp = EINA_TRUE;
+               eina_stringshare_del(c_info->full_name);
+               c_info->full_name = NULL;
+       }
+
+       if (eina_stringshare_replace(&c_info->last_name, l_name)) {
+               disp = EINA_TRUE;
+               eina_stringshare_del(c_info->full_name);
+               c_info->full_name = NULL;
+       }
+
+       disp |= eina_stringshare_replace(&c_info->picture, img);
+
+       disp |= _contact_phone_changed(c_info, contact);
+
+       if (disp)
+               _contact_info_on_changed_dispatch(c_info);
+
+       free(img);
+       free(l_name);
+       free(f_name);
+err_contact:
+       contact_destroy(contact);
+       return EINA_TRUE;
+
+deleted:
+       *deleted = eina_list_append(*deleted, c_info);
+       return EINA_TRUE;
+}
+
+static void _contact_db_changed(void *data)
+{
+       Contacts *contacts = data;
+       Contact_Info *c_info;
+       Eina_List *deleted = NULL;
+
+       EINA_SAFETY_ON_NULL_RETURN(contacts);
+       eina_hash_foreach(contacts->hash_ids, _hash_foreach, &deleted);
+
+       EINA_LIST_FREE(deleted, c_info) {
+               Contact_Number *cn;
+               /* _contact_info_free() will free the lists for me */
+               EINA_INLIST_FOREACH(c_info->alias, cn)
+                       _contact_number_entry_del(cn->number, c_info);
+               EINA_INLIST_FOREACH(c_info->numbers, cn)
+                       _contact_number_entry_del(cn->number, c_info);
+               eina_hash_del_by_key(contacts->hash_ids, &c_info->id);
+       }
+}
+
+static void _contact_number_add(char *number,
+                               Contact_Info *c_info,
+                               contact_number_h number_h)
+{
+       unsigned int numberlen = strlen(number);
+       Contact_Number *cn = malloc(sizeof(Contact_Number) + numberlen + 1);
+       EINA_SAFETY_ON_NULL_RETURN(cn);
+       memcpy(cn->number, number, numberlen);
+       cn->numberlen = numberlen;
+       cn->number[numberlen] = '\0';
+       cn->type = phone_type_get(number_h);
+       c_info->numbers = eina_inlist_append(c_info->numbers,
+                                               EINA_INLIST_GET(cn));
+}
+
+bool _search_cb(contact_query_number_s *query, void *data)
+{
+       Contact_Info **c_info = data;
+       char *n;
+       contact_h tizen_c;
+       contact_number_iterator_h it;
+       contact_number_h number_h;
+
+       *c_info = calloc(1, sizeof(Contact_Info));
+       EINA_SAFETY_ON_NULL_RETURN_VAL((*c_info), false);
+       (*c_info)->first_name = eina_stringshare_add(query->first_name);
+       (*c_info)->last_name = eina_stringshare_add(query->last_name);
+       (*c_info)->picture = eina_stringshare_add(query->contact_image_path);
+       (*c_info)->id = query->contact_db_id;
+       if (contact_get_from_db((*c_info)->id, &tizen_c) != CONTACTS_ERROR_NONE)
+               return false;
+
+       if (contact_get_number_iterator(tizen_c, &it) !=
+               CONTACTS_ERROR_NONE)
+               return false;
+
+       while (contact_number_iterator_has_next(it)) {
+               if (contact_number_iterator_next(&it, &number_h) !=
+                       CONTACTS_ERROR_NONE)
+                       continue;
+               if (contact_number_get_number(number_h, &n) !=
+                       CONTACTS_ERROR_NONE)
+                       continue;
+               _contact_number_add(n, (*c_info), number_h);
+               free(n);
+       }
+       contact_destroy(tizen_c);
+       return false;
+}
+
+static const char *phone_type_get(contact_number_h number)
+{
+       contact_number_type_e type_e;
+
+       if (contact_number_get_type(number, &type_e) < 0)
+               return NULL;
+
+       switch (type_e) {
+       case CONTACT_NUMBER_TYPE_NONE:
+               return "None";
+       case CONTACT_NUMBER_TYPE_HOME:
+               return "Home";
+       case CONTACT_NUMBER_TYPE_WORK:
+               return "Work";
+       case CONTACT_NUMBER_TYPE_VOICE:
+               return "Home";
+       case CONTACT_NUMBER_TYPE_FAX:
+               return "Fax";
+       case CONTACT_NUMBER_TYPE_MSG:
+               return "Message";
+       case CONTACT_NUMBER_TYPE_CELL:
+               return "Mobile";
+       case CONTACT_NUMBER_TYPE_PAGER:
+               return "Pager";
+       case CONTACT_NUMBER_TYPE_BBS:
+               return "Bulletin board";
+       case CONTACT_NUMBER_TYPE_MODEM:
+               return "Modem";
+       case CONTACT_NUMBER_TYPE_CAR:
+               return "Car phone";
+       case CONTACT_NUMBER_TYPE_ISDN:
+               return "ISDN";
+       case CONTACT_NUMBER_TYPE_VIDEO:
+               return "Video conference";
+       case CONTACT_NUMBER_TYPE_PCS:
+               return "Personal communicatior";
+       case CONTACT_NUMBER_TYPE_ASSISTANT:
+               return "Assistant telephone";
+       case CONTACT_NUMBER_TYPE_CUSTOM:
+               return "Custom";
+       default:
+               return "Unknown";
+       }
+}
+
+static const char *_alias_phone_type_match(Contact_Info *c_info,
+                                               const char *alias)
+{
+       Contact_Number *cn;
+       EINA_INLIST_FOREACH(c_info->numbers, cn) {
+               if (_is_alias(cn, alias))
+                       return cn->type;
+       }
+       return "Unknown";
+}
+
+static Eina_Bool _alias_create(Contact_Info *c_info, const char *number)
+{
+       unsigned int numberlen = strlen(number);
+       Contact_Number *cn = malloc(sizeof(Contact_Number) + numberlen + 1);
+       EINA_SAFETY_ON_NULL_RETURN_VAL(cn, EINA_FALSE);
+       memcpy(cn->number, number, numberlen);
+       cn->numberlen = numberlen;
+       cn->number[numberlen] = '\0';
+       cn->type = _alias_phone_type_match(c_info, number);
+       c_info->alias = eina_inlist_append(
+               c_info->alias, EINA_INLIST_GET(cn));
+       _contact_number_entry_add(cn->number, c_info);
+       return EINA_TRUE;
+}
+
+Contact_Info *contact_search(Evas_Object *obj, const char *number,
+                               const char **type)
+{
+       Contact_Info *c_info = NULL, *found;
+       Contacts *contacts;
+       Contact_Number_Entry *entry;
+
+       EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
+       EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
+       contacts = evas_object_data_get(obj, "contacts.ctx");
+       EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
+
+       entry = eina_hash_find(contacts->numbers, number);
+
+       if (entry) {
+               c_info = eina_list_data_get(entry->contacts);
+               EINA_SAFETY_ON_NULL_RETURN_VAL(c_info, NULL);
+               goto get_type;
+       }
+
+       if (contact_query_contact_by_number(_search_cb, number, &c_info) < 0) {
+               ERR("Could not fetch phone number: %s from DB", number);
+               return NULL;
+       }
+
+       if (!c_info)
+               return NULL;
+
+       /* Do we have this contact already ? */
+       found = eina_hash_find(contacts->hash_ids, &c_info->id);
+
+       if (found) {
+               _contact_info_free(c_info);
+               c_info = found;
+               /* The user enterer an alias */
+               Eina_Bool r;
+               r = _alias_create(c_info, number);
+               EINA_SAFETY_ON_FALSE_RETURN_VAL(r, NULL);
+       } else {
+               /* New contact */
+               c_info->contacts = contacts;
+               eina_hash_add(contacts->hash_ids, &c_info->id, c_info);
+               Contact_Number *cn;
+               EINA_INLIST_FOREACH(c_info->numbers, cn)
+                       _contact_number_entry_add(cn->number, c_info);
+       }
+
+get_type:
+       if (type)
+               *type = contact_info_number_check(c_info, number);
+       return c_info;
+}
+
+const char *contact_info_picture_get(const Contact_Info *c)
+{
+       EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
+       return c->picture;
+}
+
+const char *contact_info_full_name_get(const Contact_Info *c)
+{
+       Contact_Info *c2;
+       EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
+       if (!c->full_name) {
+               c2 = (Contact_Info *)c;
+               c2->full_name = eina_stringshare_printf("%s %s", c->first_name,
+                                                       c->last_name);
+       }
+       return c->full_name;
+}
+
+const char *contact_info_first_name_get(const Contact_Info *c)
+{
+       EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
+       return c->first_name;
+}
+
+const char *contact_info_last_name_get(const Contact_Info *c)
+{
+       EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
+       return c->last_name;
+}
+
+const char *contact_info_detail_get(const Contact_Info *c,
+                                       const char *type)
+{
+       EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
+       EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL);
+       Contact_Number *cn;
+       /* Do not check in the alias list. Because here we want in fact a
+        * "working" numbers
+        */
+       EINA_INLIST_FOREACH(c->numbers, cn) {
+               if (strcmp(cn->type, type) == 0)
+                       return cn->number;
+       }
+       return NULL;
+}
+
+const char *contact_info_number_check(const Contact_Info *c,
+                                       const char *number)
+{
+       EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
+       EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
+       Contact_Number *cn;
+
+       EINA_INLIST_FOREACH(c->numbers, cn) {
+               if (_contact_number_is_equal(cn, number))
+                       return cn->type;
+       }
+       /* It could be an alias */
+       EINA_INLIST_FOREACH(c->alias, cn) {
+               if (_contact_number_is_equal(cn, number))
+                       return cn->type;
+       }
+       return "Unknown";
+}
+
+Eina_Bool contact_info_picture_set(Contact_Info *c __UNUSED__,
+                                       const char *filename __UNUSED__)
+{
+       ERR("TODO");
+       return EINA_FALSE;
+}
+
+Eina_Bool contact_info_first_name_set(Contact_Info *c __UNUSED__,
+                                       const char *name __UNUSED__)
+{
+       ERR("TODO");
+       return EINA_FALSE;
+}
+
+Eina_Bool contact_info_last_name_set(Contact_Info *c __UNUSED__,
+                                       const char *name __UNUSED__)
+{
+       ERR("TODO");
+       return EINA_FALSE;
+}
+
+Eina_Bool contact_info_detail_set(Contact_Info *c __UNUSED__,
+                                       const char *type __UNUSED__,
+                                       const char *number __UNUSED__)
+{
+       ERR("TODO");
+       return EINA_FALSE;
+}
+
+void contact_info_on_changed_callback_add(Contact_Info *c,
+                                               void (*cb)(void *data, Contact_Info *c),
+                                               const void *data )
+{
+       Contact_Info_On_Changed_Ctx *ctx;
+
+       EINA_SAFETY_ON_NULL_RETURN(c);
+       EINA_SAFETY_ON_NULL_RETURN(cb);
+
+       ctx = calloc(1, sizeof(Contact_Info_On_Changed_Ctx));
+       EINA_SAFETY_ON_NULL_RETURN(ctx);
+       ctx->cb = cb;
+       ctx->data = data;
+
+       c->on_changed_cbs.listeners = eina_inlist_append(
+               c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
+}
+
+void contact_info_on_changed_callback_del(Contact_Info *c,
+                                               void (*cb)(void *data, Contact_Info *c),
+                                               const void *data)
+{
+
+       Contact_Info_On_Changed_Ctx *ctx, *found = NULL;
+       EINA_SAFETY_ON_NULL_RETURN(c);
+       EINA_SAFETY_ON_NULL_RETURN(cb);
+
+       EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
+               if (ctx->cb == cb && ctx->data == data) {
+                       found = ctx;
+                       break;
+               }
+       }
+
+       if (!found)
+               return;
+
+       if (c->on_changed_cbs.walking > 0) {
+               found->deleted = EINA_TRUE;
+               c->on_changed_cbs.deleted = eina_list_append(
+                       c->on_changed_cbs.deleted, found);
+               return;
+       }
+
+       c->on_changed_cbs.listeners = eina_inlist_remove(
+               c->on_changed_cbs.listeners, EINA_INLIST_GET(found));
+       free(found);
+}
+
+void contact_info_on_del_callback_add(Contact_Info *c,
+                                       void (*cb)(void *data, const Contact_Info *c),
+                                       const void *data)
+{
+
+       Contact_Info_On_Del_Ctx *ctx;
+
+       EINA_SAFETY_ON_NULL_RETURN(c);
+       EINA_SAFETY_ON_NULL_RETURN(cb);
+
+       ctx = calloc(1, sizeof(Contact_Info_On_Del_Ctx));
+       EINA_SAFETY_ON_NULL_RETURN(ctx);
+       ctx->cb = cb;
+       ctx->data = data;
+
+       c->on_del_cbs = eina_inlist_append(c->on_del_cbs, EINA_INLIST_GET(ctx));
+}
+
+void contact_info_on_del_callback_del(Contact_Info *c,
+                                       void (*cb)(void *data, const Contact_Info *c),
+                                       const void *data )
+{
+
+       Contact_Info_On_Del_Ctx *ctx, *found = NULL;
+       EINA_SAFETY_ON_NULL_RETURN(c);
+       EINA_SAFETY_ON_NULL_RETURN(cb);
+
+       EINA_INLIST_FOREACH(c->on_del_cbs, ctx) {
+               if (ctx->cb == cb && ctx->data == data) {
+                       found = ctx;
+                       break;
+               }
+       }
+
+       if (!found)
+               return;
+
+       c->on_del_cbs = eina_inlist_remove(c->on_del_cbs,
+                                               EINA_INLIST_GET(found));
+
+       free(found);
+}
+
+void contact_info_del(Contact_Info *c_info __UNUSED__)
+{
+       ERR("TODO");
+}
+
+static void _contacts_ug_layout_create(struct ui_gadget *ug,
+                                       enum ug_mode mode __UNUSED__,
+                                       void *priv)
+{
+       Contacts *contacts = priv;
+       EINA_SAFETY_ON_NULL_RETURN(ug);
+       elm_object_part_content_set(contacts->self, "elm.swallow.genlist",
+                                       ug_get_layout(ug));
+}
+
+static void _contact_info_free(Contact_Info *c_info)
+{
+       Contact_Number *cn;
+       _contact_info_on_del_dispatch(c_info);
+       EINA_SAFETY_ON_FALSE_RETURN(c_info->on_changed_cbs.walking == 0);
+
+       if (c_info->on_changed_cbs.deleted) {
+               ERR("contact still have changed deleted listeners: %p %s %s",
+                       c_info, c_info->first_name, c_info->last_name);
+               eina_list_free(c_info->on_changed_cbs.deleted);
+       }
+
+       if (c_info->on_changed_cbs.listeners) {
+               while (c_info->on_changed_cbs.listeners) {
+                       Contact_Info_On_Changed_Ctx *ctx;
+
+                       ctx = EINA_INLIST_CONTAINER_GET(
+                               c_info->on_changed_cbs.listeners,
+                               Contact_Info_On_Changed_Ctx);
+                       c_info->on_changed_cbs.listeners = eina_inlist_remove
+                               (c_info->on_changed_cbs.listeners,
+                                       c_info->on_changed_cbs.listeners);
+                       free(ctx);
+               }
+       }
+
+       while (c_info->numbers) {
+               cn = EINA_INLIST_CONTAINER_GET(c_info->numbers,
+                                               Contact_Number);
+               c_info->numbers = eina_inlist_remove(c_info->numbers,
+                                                       c_info->numbers);
+               free(cn);
+       }
+
+       while (c_info->alias) {
+               cn = EINA_INLIST_CONTAINER_GET(c_info->alias,
+                                               Contact_Number);
+               c_info->alias = eina_inlist_remove(c_info->alias,
+                                                       c_info->alias);
+               free(cn);
+       }
+
+       eina_stringshare_del(c_info->first_name);
+       eina_stringshare_del(c_info->last_name);
+       eina_stringshare_del(c_info->full_name);
+       eina_stringshare_del(c_info->picture);
+       free(c_info);
+}
+
+void _hash_elements_free(void *data)
+{
+       Contact_Info *c = data;
+       _contact_info_free(c);
+}
+
+void _numbers_hash_elements_free(void *data)
+{
+       Contact_Number_Entry *e = data;
+       /*Contact_Info will be deleted by hash_ids */
+       eina_list_free(e->contacts);
+       free(e);
+}
+
+static void _on_del(void *data, Evas *e __UNUSED__,
+                       Evas_Object *obj __UNUSED__, void *event __UNUSED__)
+{
+       Contacts *contacts = data;
+       eina_hash_free(contacts->hash_ids);
+       eina_hash_free(contacts->numbers);
+       ug_destroy(contacts->ug_all);
+       free(contacts);
+       contacts_disconnect();
+}
+
+Evas_Object *contacts_add(Evas_Object *parent)
+{
+       struct ug_cbs cbs;
+       struct ui_gadget *ug;
+       bundle *bd;
+       char buf[PATH_MAX];
+       Contacts *contacts;
+
+       if (contacts_connect() < 0) {
+               ERR("Could not connect to the contacts DB");
+               return NULL;
+       }
+
+       contacts = calloc(1, sizeof(Contacts));
+       EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
+       contacts->self = gui_layout_add(parent, "contacts_bg");
+       EINA_SAFETY_ON_NULL_GOTO(contacts->self, err_layout);
+       evas_object_event_callback_add(contacts->self, EVAS_CALLBACK_DEL,
+                                       _on_del, contacts);
+
+       contacts->numbers = eina_hash_string_superfast_new(
+               _numbers_hash_elements_free);
+       EINA_SAFETY_ON_NULL_GOTO(contacts->numbers, err_hash);
+
+       contacts->hash_ids = eina_hash_int32_new(_hash_elements_free);
+       EINA_SAFETY_ON_NULL_GOTO(contacts->hash_ids, err_hash_id);
+
+       cbs.priv = contacts;
+       cbs.layout_cb = _contacts_ug_layout_create;
+       cbs.result_cb = NULL;
+       cbs.destroy_cb = NULL;
+
+       bd = bundle_create();
+       EINA_SAFETY_ON_NULL_GOTO(bd, err_bd);
+
+       snprintf(buf, sizeof(buf), "%d", CT_UG_REQUEST_DETAIL);
+       bundle_add(bd, CT_UG_BUNDLE_TYPE, buf);
+       snprintf(buf, sizeof(buf), "%d", 1);
+       bundle_add(bd, CT_UG_BUNDLE_ID, buf);
+
+       ug = ug_create(NULL, UG_CONTACTS_LIST, UG_MODE_FRAMEVIEW, bd, &cbs);
+       EINA_SAFETY_ON_NULL_GOTO(ug, err_ug);
+
+       bundle_free(bd);
+       bd = NULL;
+
+       contacts->ug_all = ug;
+
+       contacts_add_contact_db_changed_cb(_contact_db_changed, contacts);
+
+       evas_object_data_set(contacts->self, "contacts.ctx", contacts);
+       return contacts->self;
+
+err_ug:
+       if (bd)
+               bundle_free(bd);
+err_bd:
+       eina_hash_free(contacts->hash_ids);
+err_hash_id:
+       eina_hash_free(contacts->numbers);
+err_hash:
+       evas_object_del(contacts->self);
+err_layout:
+       free(contacts);
+       return NULL;
+}
index f4e287e..ace0012 100644 (file)
 #include "ussd.h"
 #include "util.h"
 
+#ifdef HAVE_TIZEN
+#include <appcore-efl.h>
+#include <ui-gadget.h>
+#include <Ecore_X.h>
+#endif
+
 static Evas_Object *win = NULL;
 static Evas_Object *main_layout = NULL;
 static Evas_Object *keypad = NULL;
@@ -522,6 +528,11 @@ Eina_Bool gui_init(const char *theme)
        EINA_SAFETY_ON_NULL_RETURN_VAL(win, EINA_FALSE);
        elm_win_autodel_set(win, EINA_FALSE);
 
+#ifdef HAVE_TIZEN
+       appcore_set_i18n("ofono-efl", "en-US");
+       UG_INIT_EFL(win, UG_OPT_INDICATOR_PORTRAIT_ONLY);
+#endif
+
        flip = elm_flip_add(win);
        evas_object_size_hint_weight_set(flip,
                                EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);