From 130294305c1907c6cfc7ae87b7f700b2c7492d71 Mon Sep 17 00:00:00 2001 From: Iscaro Date: Fri, 3 Aug 2012 11:33:57 -0300 Subject: [PATCH] Contacts UI and infrastructure. TODO: Change the default elm_button style --- Makefile.am | 2 + data/themes/default.edc | 2 + data/themes/includes/contacts-bg.edc | 174 ++++++++++++++++++++ data/themes/includes/contacts.edc | 304 +++++++++++++++++++++++++++++++++++ data/themes/includes/scroller.edc | 1 + dialer/contacts.c | 280 +++++++++++++++++++++++++++++++- dialer/contacts.h | 8 + 7 files changed, 769 insertions(+), 2 deletions(-) create mode 100644 data/themes/includes/contacts-bg.edc create mode 100644 data/themes/includes/contacts.edc diff --git a/Makefile.am b/Makefile.am index 5cbd4e9..27718a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -97,6 +97,8 @@ data/themes/images/bt_swap_released.png THEME_INCLUDES = \ data/themes/includes/call.edc \ data/themes/includes/colors.edc \ +data/themes/includes/contacts-bg.edc \ +data/themes/includes/contacts.edc \ data/themes/includes/history-bg.edc \ data/themes/includes/history.edc \ data/themes/includes/keypad.edc \ diff --git a/data/themes/default.edc b/data/themes/default.edc index b868d6d..51fc741 100644 --- a/data/themes/default.edc +++ b/data/themes/default.edc @@ -4,6 +4,8 @@ collections { #include "includes/main.edc" #include "includes/keypad.edc" #include "includes/call.edc" +#include "includes/contacts-bg.edc" +#include "includes/contacts.edc" #include "includes/multiparty-list.edc" #include "includes/history.edc" #include "includes/history-bg.edc" diff --git a/data/themes/includes/contacts-bg.edc b/data/themes/includes/contacts-bg.edc new file mode 100644 index 0000000..97ba108 --- /dev/null +++ b/data/themes/includes/contacts-bg.edc @@ -0,0 +1,174 @@ +group { + name: "elm/layout/dialer/contacts_bg"; + + images { + image: "bg_keypad.jpg" COMP; + } + + parts { + + part { + name: "bg"; + type: IMAGE; + mouse_events: 0; + description { + state: "default" 0.0; + rel1.relative: 0.0 0.0; + rel2.relative: 1.0 1.0; + color: 255 255 255 255; + color_class: "dark"; + image { + normal: "bg_keypad.jpg"; + scale_hint: STATIC; + } + fill.smooth: 1; + } + } + + part { + name: "text.title"; + type: TEXT; + mouse_events: 0; + scale: 1; + description { + state: "default" 0.0; + color: 255 255 255 255; + color_class: "action"; + min: WIDTH ACTION_HEIGHT; + rel2 { + relative: 1.0 0.0; + offset: -1.0 (ACTION_HEIGHT - 1); + } + text { + text: "Contacts"; + font: FONT; + size: SIZE_HUGE; + size_range: SIZE_TINY SIZE_HUGE; + fit: 1 1; + align: 0.5 0.5; + ellipsis: 1.0; + } + } + } +#define SEPARATOR(id, rely, offy, relto) \ + part { \ + name: "separator.dark."##id; \ + type: RECT; \ + mouse_events: 0; \ + description { \ + state: "default" 0.0; \ + color: 255 255 255 255; \ + color_class: "dark"; \ + rel1 { \ + relative: 0.0 rely; \ + offset: 0 offy; \ + to_y: relto; \ + } \ + rel2 { \ + relative: 1.0 rely; \ + offset: -1 (offy + SEPARATOR_HEIGHT / 2); \ + to_y: relto; \ + } \ + } \ + } \ + part { \ + name: "separator.bg."##id; \ + type: RECT; \ + mouse_events: 0; \ + description { \ + state: "default" 0.0; \ + color: 255 255 255 255; \ + color_class: "bg"; \ + rel1 { \ + relative: 0.0 rely; \ + offset: 0 (offy + SEPARATOR_HEIGHT / 2); \ + to_y: relto; \ + } \ + rel2 { \ + relative: 1.0 rely; \ + offset: -1 (offy + SEPARATOR_HEIGHT); \ + to_y: relto; \ + } \ + } \ + } + + SEPARATOR("title", 1.0, -SEPARATOR_HEIGHT, "text.title"); +#undef SEPARATOR + part { + name: "elm.swallow.details"; + type: SWALLOW; + description { + state: "default" 0.0; + visible: 0; + rel1 { + relative: 0.0 1.0; + to_y: "text.title"; + } + } + description { + state: "show" 0.0; + inherit: "default" 0.0; + visible: 1; + } + } + + part { + name: "elm.swallow.genlist"; + type: SWALLOW; + description { + state: "default" 0.0; + visible: 1; + rel1 { + relative: 0.0 1.0; + to_y: "text.title"; + } + } + description { + state: "hide" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + } + + programs { + + program { + name: "hide,genlist"; + action: STATE_SET "hide" 0.0; + target: "elm.swallow.genlist"; + } + + program { + name: "show,genlist"; + action: STATE_SET "default" 0.0; + target: "elm.swallow.genlist"; + } + + program { + name: "hide,details"; + action: STATE_SET "default" 0.0; + target: "elm.swallow.details"; + } + + program { + name: "show,details"; + action: STATE_SET "show" 0.0; + target: "elm.swallow.details"; + } + + program { + signal: "show,details"; + source: "gui"; + after: "hide,genlist"; + after: "show,details"; + } + + program { + signal: "hide,details"; + source: "gui"; + after: "hide,details"; + after: "show,genlist"; + } + } +} diff --git a/data/themes/includes/contacts.edc b/data/themes/includes/contacts.edc new file mode 100644 index 0000000..c450db1 --- /dev/null +++ b/data/themes/includes/contacts.edc @@ -0,0 +1,304 @@ +group { + name: "elm/genlist/item/contacts/default"; + + data { + item: "texts" "text.contacts.name"; + } + + parts{ + part { + name: "bg"; + type: RECT; + mouse_events: 1; + description { + state: "default" 0.0; + color: 255 255 255 0; + color_class: "action"; + min: ACTION_WIDTH LIST_ITEM_HEIGHT; + } + description { + state: "selected" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + } + + part { + name: "text.contacts.name"; + type: TEXT; + scale: 1; + mouse_events: 0; + description { + state: "default" 0.0; + color: 255 255 255 255; + color_class: "action"; + rel1 { + relative: 0.0 0.0; + offset: BORDER_PADDING 0; + } + text { + font: FONT_NORMAL; + size: SIZE_MEDIUM; + size_range: SIZE_SMALL SIZE_MEDIUM; + fit: 1 1; + align: 0.0 0.5; + ellipsis: 0.0; + } + } + description { + state: "selected" 0.0; + inherit: "default" 0.0; + color_class: "bg"; + } + } + } + + programs { + + program { + signal: "elm,state,selected"; + source: "elm"; + action: STATE_SET "selected" 0.0; + target: "bg"; + target: "text.contacts.name"; + } + + program { + signal: "elm,state,unselected"; + source: "elm"; + action: STATE_SET "default" 0.0; + target: "bg"; + target: "text.contacts.name"; + } + } +} + +group { + name: "elm/layout/dialer/contacts_details"; + + parts { + + part { + name: "text.name"; + type: TEXT; + scale: 1; + mouse_events: 0; + description { + state: "default" 0.0; + color: 255 255 255 255; + color_class: "action"; + rel1 { + to: "img.photo"; + relative: 1.0 0.0; + offset: 16 0; + } + rel2 { + to_y: "img.photo"; + relative: 1.0 1.0; + offset: -47 -1; + } + text { + font: FONT_NORMAL; + size: SIZE_HUGE; + size_range: SIZE_LARGE SIZE_HUGE; + fit: 1 1; + align: 0.0 0.5; + ellipsis: 0.0; + } + } + } + + part { + name: "img.photo"; + type: SWALLOW; + scale: 1; + mouse_events: 0; + description { + state: "default" 0.0; + min: 170 170; + max: 170 170; + rel1{ + relative: 0.0 0.0; + offset: 46 0; + } + rel2 { + relative: 0.0 0.0; + offset: 216 170; + } + } + } +#define SEPARATOR(id, rely, offy, relto) \ + part { \ + name: "separator.dark."##id; \ + type: RECT; \ + mouse_events: 0; \ + description { \ + state: "default" 0.0; \ + color: 255 255 255 255; \ + color_class: "dark"; \ + rel1 { \ + relative: 0.0 rely; \ + offset: 0 offy; \ + to_y: relto; \ + } \ + rel2 { \ + relative: 1.0 rely; \ + offset: -1 (offy + SEPARATOR_HEIGHT / 2); \ + to_y: relto; \ + } \ + } \ + } \ + part { \ + name: "separator.bg."##id; \ + type: RECT; \ + mouse_events: 0; \ + description { \ + state: "default" 0.0; \ + color: 255 255 255 255; \ + color_class: "bg"; \ + rel1 { \ + relative: 0.0 rely; \ + offset: 0 (offy + SEPARATOR_HEIGHT / 2); \ + to_y: relto; \ + } \ + rel2 { \ + relative: 1.0 rely; \ + offset: -1 (offy + SEPARATOR_HEIGHT); \ + to_y: relto; \ + } \ + } \ + } + + SEPARATOR("btn", 0.0, -SEPARATOR_HEIGHT, "bg.buttons"); +#undef SEPARATOR + + part { + name: "box.phones"; + type: BOX; + scale: 1; + mouse_events: 1; + description { + state: "default" 0.0; + box { + layout: "vertical_homogeneous"; + padding: 0 ITEM_PADDING; + } + rel1 { + to_y: "text.name"; + relative: 0.0 1.0; + offset: BORDER_PADDING ITEM_PADDING; + } + rel2 { + to_y: "bg.buttons"; + relative: 1.0 0.0; + offset: (-BORDER_PADDING-1) (-SEPARATOR_HEIGHT - 1); + } + } + } + + part { + name: "bg.buttons"; + type: RECT; + mouse_events: 0; + description { + state: "default" 0.0; + color: 0 0 0 0; + min: WIDTH ACTION_HEIGHT; + max: WIDTH 99999; /* keep it tight centered */ + rel1 { + relative: 0.0 1.0; + offset: 0 -ACTION_HEIGHT; + } + } + } +#define BUTTON(id, label, ccls, r1, r2) \ + part { \ + name: "button."##id; \ + type: RECT; \ + mouse_events: 1; \ + description { \ + state: "default" 0.0; \ + color: 255 255 255 0; \ + color_class: "action"; \ + rel1 { \ + to: "bg.buttons"; \ + relative: r1; \ + } \ + rel2 { \ + to: "bg.buttons"; \ + relative: r2; \ + } \ + } \ + description { \ + state: "pressed" 0.0; \ + inherit: "default" 0.0; \ + color: 255 255 255 255; \ + } \ + } \ + part { \ + name: "label."##id; \ + type: TEXT; \ + mouse_events: 0; \ + description { \ + state: "default" 0.0; \ + color: 255 255 255 255; \ + color_class: ccls; \ + rel1.to: "button."##id; \ + rel2 { \ + to: "button."##id; \ + } \ + text { \ + text: label; \ + font: FONT_NORMAL; \ + size: SIZE_HUGE; \ + align: 0.5 0.5; \ + } \ + } \ + description { \ + state: "pressed" 0.0; \ + inherit: "default" 0.0; \ + color: 16 16 16 255; \ + } \ + } \ + \ + programs { \ + program { \ + signal: "mouse,up,1"; \ + source: "button."##id; \ + action: SIGNAL_EMIT "released,"##id "keypad"; \ + after: "show_up_"##id; \ + api: id"_released" id" was released"; \ + } \ + program { \ + name: "show_up_"##id; \ + action: STATE_SET "default" 0.0; \ + transition: DECELERATE 0.1; \ + target: "button."##id; \ + target: "label."##id; \ + } \ + program { \ + signal: "mouse,down,1"; \ + source: "button."##id; \ + after: "show_down_"##id; \ + action: SIGNAL_EMIT "pressed,"##id "keypad"; \ + api: id"_pressed" id" was pressed"; \ + } \ + program { \ + name: "show_down_"##id; \ + action: STATE_SET "pressed" 0.0; \ + transition: ACCELERATE 0.1; \ + target: "button."##id; \ + target: "label."##id; \ + } \ + program { \ + signal: "mouse,clicked,1"; \ + source: "button."##id; \ + action: SIGNAL_EMIT "clicked,"##id "gui"; \ + api: id"_clicked" id" was clicked"; \ + } \ + } + + BUTTON("back", "Back", "action", 0.0 0.0, 1.0 1.0); +#undef BUTTON + } +} diff --git a/data/themes/includes/scroller.edc b/data/themes/includes/scroller.edc index daf09e9..a9ccc7f 100644 --- a/data/themes/includes/scroller.edc +++ b/data/themes/includes/scroller.edc @@ -1,6 +1,7 @@ group { name: "elm/scroller/base/multiparty-details"; alias: "elm/genlist/base/history"; + alias: "elm/genlist/base/contacts"; script { public sbvis_v, sbalways_v, sbvis_timer; diff --git a/dialer/contacts.c b/dialer/contacts.c index 679f906..de716b7 100644 --- a/dialer/contacts.c +++ b/dialer/contacts.c @@ -2,10 +2,286 @@ #include "config.h" #endif #include +#include +#include + +#include "log.h" +#include "gui.h" +#include "ofono.h" +#include "contacts.h" + +#ifndef EET_COMPRESSION_DEFAULT +#define EET_COMPRESSION_DEFAULT 1 +#endif + +#define CONTACTS_ENTRY "contacts" + +typedef struct _Contacts_List { + Eina_List *list; +} Contacts_List; + +typedef struct _Contacts { + char *path; + Eet_Data_Descriptor *edd; + Eet_Data_Descriptor *edd_list; + Elm_Genlist_Item_Class *itc; + Evas_Object *genlist, *layout, *details; + Contacts_List *c_list; +} Contacts; + +static void _contacts_info_descriptor_init(Eet_Data_Descriptor **edd, + Eet_Data_Descriptor **edd_list) +{ + Eet_Data_Descriptor_Class eddc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contact_Info); + *edd = eet_data_descriptor_stream_new(&eddc); + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contacts_List); + *edd_list = eet_data_descriptor_stream_new(&eddc); + + EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info, + "picture", picture, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info, + "work", work, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info, + "home", home, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info, + "mobile", mobile, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info, + "name", name, EET_T_STRING); + + EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Contacts_List, "list", list, + *edd); +} + +static void _contacts_info_free(Contact_Info *c_info) +{ + eina_stringshare_del(c_info->name); + eina_stringshare_del(c_info->mobile); + eina_stringshare_del(c_info->home); + eina_stringshare_del(c_info->work); + eina_stringshare_del(c_info->picture); + free(c_info); +} + +static void _on_del(void *data, Evas *e __UNUSED__, + Evas_Object *obj __UNUSED__, void *event __UNUSED__) +{ + Contacts *contacts = data; + Contact_Info *c_info; + eet_data_descriptor_free(contacts->edd); + eet_data_descriptor_free(contacts->edd_list); + EINA_LIST_FREE(contacts->c_list->list, c_info) + _contacts_info_free(c_info); + free(contacts->c_list); + elm_genlist_item_class_free(contacts->itc); + free(contacts->path); + free(contacts); + eet_shutdown(); +} + +static void _on_number_clicked(void *data, Evas_Object *obj __UNUSED__, + void *event_inf __UNUSED__) +{ + const char *number = data; + ofono_dial(number, NULL, NULL, NULL); +} + +static void _on_item_click(void *data, Evas_Object *obj __UNUSED__, + void *event_inf) +{ + Contacts *contacts = data; + Elm_Object_Item *item = event_inf; + Evas_Object *details, *btn; + char buf[1024]; + Contact_Info *c_info; + + details = contacts->details; + c_info = elm_object_item_data_get(item); + elm_genlist_item_selected_set(item, EINA_FALSE); + elm_layout_box_remove_all(details, "box.phones", EINA_TRUE); + + /* TODO SET IMAGE AS WELL */ + elm_object_part_text_set(details, "text.name", c_info->name); + + btn = elm_button_add(details); + EINA_SAFETY_ON_NULL_RETURN(btn); + snprintf(buf, sizeof(buf), "Mobile: %s", c_info->mobile); + elm_object_part_text_set(btn, NULL, buf); + evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(btn); + evas_object_smart_callback_add(btn, "clicked", _on_number_clicked, + c_info->mobile); + elm_layout_box_append(details, "box.phones", btn); + + btn = elm_button_add(details); + EINA_SAFETY_ON_NULL_RETURN(btn); + snprintf(buf, sizeof(buf), "Home: %s", c_info->home); + elm_object_part_text_set(btn, NULL, buf); + evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(btn); + evas_object_smart_callback_add(btn, "clicked", _on_number_clicked, + c_info->home); + elm_layout_box_append(details, "box.phones", btn); + + btn = elm_button_add(details); + EINA_SAFETY_ON_NULL_RETURN(btn); + snprintf(buf, sizeof(buf), "Work: %s", c_info->work); + elm_object_part_text_set(btn, NULL, buf); + evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(btn); + evas_object_smart_callback_add(btn, "clicked", _on_number_clicked, + c_info->work); + elm_layout_box_append(details, "box.phones", btn); + elm_object_signal_emit(contacts->layout, "show,details", "gui"); +} + +static int _sort_by_name_cb(const void *v1, const void *v2) +{ + const Contact_Info *c1, *c2; + c1 = v1; + c2 = v2; + + if (!c1) return 1; + if (!c2) return -1; + + return strcmp(c1->name, c2->name); +} + +static void _contacts_read(Contacts *contacts) +{ + Contact_Info *c_info; + Eina_List *l; + Eet_File *efile; + + efile = eet_open(contacts->path, EET_FILE_MODE_READ); + + if (efile) { + contacts->c_list = eet_data_read(efile, contacts->edd_list, + CONTACTS_ENTRY); + eet_close(efile); + } else + contacts->c_list = calloc(1, sizeof(Contacts_List)); + + EINA_SAFETY_ON_NULL_RETURN(contacts->c_list); + contacts->c_list->list = eina_list_sort(contacts->c_list->list, 0, + _sort_by_name_cb); + + EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) { + if (!c_info) + continue; + elm_genlist_item_append(contacts->genlist,contacts->itc, c_info, + NULL,ELM_GENLIST_ITEM_NONE, + _on_item_click, contacts); + } +} + +static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__, + const char *part) +{ + Contact_Info *c_info = data; + + if (strncmp(part, "text.contacts", strlen("text.contacts"))) + return NULL; + + part += strlen("text.contacts."); + + if (strcmp(part, "name") == 0) + return strdup(c_info->name); + + ERR("Unexpected part name: %s", part); + return NULL; +} + +static void _on_back_clicked(void *data, Evas_Object *obj __UNUSED__, + const char *emission __UNUSED__, + const char *source __UNUSED__) +{ + Evas_Object *layout = data; + elm_object_signal_emit(layout, "hide,details", "gui"); +} Evas_Object *contacts_add(Evas_Object *parent) { - Evas_Object *obj = elm_label_add(parent); - elm_object_text_set(obj, "TO BE DONE"); + int r; + const char *config_path; + char base_dir[PATH_MAX], *path; + Contacts *contacts; + Evas_Object *obj, *genlist, *details; + Elm_Genlist_Item_Class *itc; + + eet_init(); + contacts = calloc(1, sizeof(Contacts)); + EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL); + + + details = gui_layout_add(parent, "contacts_details"); + EINA_SAFETY_ON_NULL_GOTO(details, err_layout); + + obj = gui_layout_add(parent, "contacts_bg"); + EINA_SAFETY_ON_NULL_GOTO(obj, err_obj); + + genlist = elm_genlist_add(obj); + EINA_SAFETY_ON_NULL_GOTO(genlist, err_genlist); + elm_object_style_set(genlist, "contacts"); + + itc = elm_genlist_item_class_new(); + EINA_SAFETY_ON_NULL_GOTO(itc, err_genlist); + itc->item_style = "contacts"; + itc->func.text_get = _item_label_get; + itc->func.content_get = NULL; + itc->func.state_get = NULL; + itc->func.del = NULL; + contacts->genlist = genlist; + contacts->itc = itc; + contacts->layout = obj; + contacts->details = details; + + elm_object_part_content_set(obj, "elm.swallow.genlist", genlist); + elm_object_part_content_set(obj, "elm.swallow.details", details); + + elm_object_signal_callback_add(details, "clicked,back", "gui", + _on_back_clicked, obj); + + config_path = efreet_config_home_get(); + snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path, + PACKAGE_NAME); + ecore_file_mkpath(base_dir); + + r = asprintf(&path, "%s/%s/contacts.eet", config_path, PACKAGE_NAME); + + if (r < 0) + goto err_path; + + contacts->path = path; + + _contacts_info_descriptor_init(&contacts->edd, &contacts->edd_list); + _contacts_read(contacts); + EINA_SAFETY_ON_NULL_GOTO(contacts->c_list, err_read); + + evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del, + contacts); return obj; + +err_read: + free(contacts->path); + eet_data_descriptor_free(contacts->edd); + eet_data_descriptor_free(contacts->edd_list); +err_path: + elm_genlist_item_class_free(itc); +err_genlist: + free(obj); +err_obj: + free(details); +err_layout: + free(contacts); + eet_shutdown(); + return NULL; } diff --git a/dialer/contacts.h b/dialer/contacts.h index b97f423..06b8416 100644 --- a/dialer/contacts.h +++ b/dialer/contacts.h @@ -1,6 +1,14 @@ #ifndef _EFL_OFONO_CONTACTS_H__ #define _EFL_OFONO_CONTACTS_H__ 1 +typedef struct _Contact_Info { + const char *name; + const char *mobile; + const char *home; + const char *work; + const char *picture; +} Contact_Info; + Evas_Object *contacts_add(Evas_Object *parent); #endif -- 2.7.4