Changing default layout of the history UI.
[profile/ivi/lemolo.git] / dialer / history.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include <Elementary.h>
5 #include <Eet.h>
6 #include <Eina.h>
7 #include <time.h>
8 #include <limits.h>
9 #include <string.h>
10
11 #include "ofono.h"
12 #include "log.h"
13 #include "util.h"
14 #include "gui.h"
15
16 #define HISTORY_ENTRY "history"
17
18 typedef struct _Call_Info_List {
19         Eina_List *list;
20 } Call_Info_List;
21
22 typedef struct _History {
23         Eet_File *log;
24         Eet_Data_Descriptor *edd;
25         Eet_Data_Descriptor *edd_list;
26         Call_Info_List *calls;
27         Elm_Genlist_Item_Class *itc;
28         Evas_Object *genlist_all, *genlist_missed;
29 } History;
30
31 typedef struct _Call_Info {
32         int state;
33         long long start_time;
34         long long end_time;
35         const char *line_id;
36         const char *name;
37 } Call_Info;
38
39 static OFono_Callback_List_Call_Node *callback_node_call_removed = NULL;
40 static OFono_Callback_List_Call_Node *callback_node_call_changed = NULL;
41
42 static Call_Info *_history_call_info_list_search(Eina_List *list,
43                                                         const char *line_id)
44 {
45         Call_Info *call_info;
46         Eina_List *l;
47
48         EINA_LIST_FOREACH(list, l, call_info)
49                 if (!strcmp(call_info->line_id, line_id))
50                         return call_info;
51
52         return NULL;
53 }
54
55 static void _history_call_changed(void *data, OFono_Call *call)
56 {
57         History *history = data;
58         const char *line_id = ofono_call_line_id_get(call);
59         Call_Info *call_info;
60         OFono_Call_State state = ofono_call_state_get(call);
61
62         call_info =
63                 _history_call_info_list_search(history->calls->list, line_id);
64
65         if (call_info) {
66                 /* Otherwise I missed the call or the person didn't
67                  * awnser my call!
68                  */
69                 if ((call_info->state == OFONO_CALL_STATE_INCOMING ||
70                         call_info->state == OFONO_CALL_STATE_DIALING) &&
71                         state != OFONO_CALL_STATE_DISCONNECTED)
72                         call_info->state = state;
73                 return;
74         }
75         call_info = calloc(1, sizeof(Call_Info));
76         EINA_SAFETY_ON_NULL_RETURN(call_info);
77
78         call_info->state = state;
79         call_info->start_time = time(NULL);
80         call_info->line_id = eina_stringshare_add(line_id);
81         call_info->name = eina_stringshare_add(ofono_call_name_get(call));
82         history->calls->list =
83                 eina_list_prepend(history->calls->list, call_info);
84 }
85
86 static void _history_call_log_save(History *history)
87 {
88         if (!(eet_data_write(history->log,
89                                 history->edd_list, HISTORY_ENTRY,
90                                 history->calls, EET_COMPRESSION_DEFAULT)))
91                 ERR("Could in the history log file");
92 }
93
94 static void _history_call_removed(void *data, OFono_Call *call)
95 {
96         History *history = data;
97         const char *line_id = ofono_call_line_id_get(call);
98         Call_Info *call_info;
99         time_t start;
100         char *tm;
101
102         call_info =
103                 _history_call_info_list_search(history->calls->list, line_id);
104         EINA_SAFETY_ON_NULL_RETURN(call_info);
105         start = call_info->start_time;
106         tm = ctime(&start);
107
108         if (call_info->state == OFONO_CALL_STATE_INCOMING) {
109                 INF("Missed call - Id: %s - time: %s", line_id, tm);
110                 elm_genlist_item_append(history->genlist_missed, history->itc,
111                                         call_info, NULL, ELM_GENLIST_ITEM_NONE,
112                                         NULL, NULL);
113         } else if (call_info->state == OFONO_CALL_STATE_DIALING)
114                 INF("Call not answered - Id: %s - time: %s", line_id, tm);
115         else
116                 INF("A call has ended - Id: %s - time: %s", line_id, tm);
117
118         call_info->end_time = time(NULL);
119         _history_call_log_save(history);
120         elm_genlist_item_append(history->genlist_all, history->itc, call_info,
121                                 NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
122 }
123
124 static void _call_info_free(Call_Info *call_info)
125 {
126         eina_stringshare_del(call_info->line_id);
127         eina_stringshare_del(call_info->name);
128         free(call_info);
129 }
130
131 static void _on_del(void *data, Evas *e __UNUSED__,
132                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
133 {
134         History *history = data;
135         Call_Info *call_info;
136         ofono_call_removed_cb_del(callback_node_call_removed);
137         ofono_call_changed_cb_del(callback_node_call_changed);
138         eet_close(history->log);
139         eet_data_descriptor_free(history->edd);
140         eet_data_descriptor_free(history->edd_list);
141         EINA_LIST_FREE(history->calls->list, call_info)
142                 _call_info_free(call_info);
143         free(history->calls);
144         elm_genlist_item_class_free(history->itc);
145         free(history);
146         eet_shutdown();
147 }
148
149 static void _history_call_info_descriptor_init(Eet_Data_Descriptor **edd,
150                                                 Eet_Data_Descriptor **edd_list)
151 {
152         Eet_Data_Descriptor_Class eddc;
153
154         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info);
155         *edd = eet_data_descriptor_stream_new(&eddc);
156
157         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info_List);
158         *edd_list = eet_data_descriptor_stream_new(&eddc);
159
160         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
161                                         "state", state, EET_T_INT);
162         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
163                                         "start_time", start_time, EET_T_LONG_LONG);
164         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
165                                         "end_time", end_time, EET_T_LONG_LONG);
166         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
167                                         "line_id", line_id, EET_T_STRING);
168         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
169                                         "name", name, EET_T_STRING);
170
171         EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Call_Info_List, "list", list,
172                                         *edd);
173 }
174
175 static void _history_call_log_read(History *history)
176 {
177         Call_Info *call_info;
178         Eina_List *l;
179
180         history->calls = eet_data_read(history->log, history->edd_list,
181                                                         HISTORY_ENTRY);
182
183         if (!history->calls) {
184                 history->calls = calloc(1, sizeof(Call_Info_List));
185                 return;
186         }
187
188         EINA_LIST_FOREACH(history->calls->list, l, call_info) {
189                 if (!call_info)
190                         continue;
191                 elm_genlist_item_append(history->genlist_all, history->itc,
192                                         call_info, NULL, ELM_GENLIST_ITEM_NONE,
193                                         NULL, NULL);
194                 if (call_info->state == OFONO_CALL_STATE_INCOMING)
195                         elm_genlist_item_append(history->genlist_missed,
196                                                 history->itc, call_info, NULL,
197                                                 ELM_GENLIST_ITEM_NONE,
198                                                 NULL, NULL);
199         }
200 }
201
202 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
203                                 const char *part)
204 {
205         Call_Info *call_info = data;
206
207         if (strncmp(part, "text.call", strlen("text.call")))
208                 return NULL;
209
210         part += strlen("text.call.");
211
212         if (!strcmp(part, "name")) {
213                 const char *name;
214                 name = call_info->name;
215                 if (!call_info->name || call_info->name[0] == '\0')
216                         name = call_info->line_id;
217                 return strdup(name);
218         }
219
220         if (!strcmp(part, "time"))
221                 return date_format(call_info->end_time);
222
223         /* TODO: Fetch phone type from contacts information*/
224         if (!strcmp(part, "type"))
225                 return strdup("TODO:TELEPHONE TYPE");
226
227         ERR("Unexpected text part: %s", part);
228         return NULL;
229 }
230
231
232 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
233                                         const char  *part)
234 {
235         Call_Info *call_info = data;
236
237         if (!strcmp(part, "missed")) {
238                 if (call_info->state == OFONO_CALL_STATE_INCOMING ||
239                         call_info->state == OFONO_CALL_STATE_DIALING)
240                         return EINA_TRUE;
241                 else
242                         return EINA_FALSE;
243         } else if (!strcmp(part, "completed")) {
244                 if (call_info->state == OFONO_CALL_STATE_INCOMING ||
245                         call_info->state == OFONO_CALL_STATE_DIALING)
246                         return EINA_FALSE;
247                 else
248                         return EINA_TRUE;
249         } else if (!strcmp(part, "outgoing"))
250                 return EINA_TRUE;
251         else if (!strcmp(part, "incoming"))
252                 return EINA_TRUE;
253
254         ERR("Unexpected state part: %s", part);
255         return EINA_FALSE;
256 }
257
258 static void _on_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
259                         const char *emission, const char *source __UNUSED__)
260 {
261         EINA_SAFETY_ON_NULL_RETURN(emission);
262         emission += strlen("clicked,");
263
264         if (!strcmp(emission, "all"))
265                 elm_object_signal_emit(obj, "show,all", "gui");
266         else
267                 elm_object_signal_emit(obj, "show,missed", "gui");
268 }
269
270 static void _on_more_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
271                                 const char *emission __UNUSED__,
272                                 const char *source __UNUSED__)
273 {
274         DBG("TODO");
275 }
276
277 static Evas_Object *_item_content_get(void *data __UNUSED__, Evas_Object *obj,
278                                         const char *part __UNUSED__)
279 {
280         Evas_Object *btn;
281
282         btn = gui_layout_add(obj, "history/img");
283         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
284         elm_object_signal_callback_add(btn, "clicked,more", "gui",
285                                         _on_more_clicked, NULL);
286
287         return btn;
288 }
289
290 Evas_Object *history_add(Evas_Object *parent)
291 {
292         History *history;
293         const char *config_path;
294         char path[PATH_MAX];
295         Elm_Genlist_Item_Class *itc;
296         Evas_Object *obj, *genlist_all, *genlist_missed;
297
298         eet_init();
299         history = calloc(1, sizeof(History));
300         EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
301
302         obj = gui_layout_add(parent, "history_bg");
303         EINA_SAFETY_ON_NULL_GOTO(obj, err_layout);
304
305         genlist_all = elm_genlist_add(obj);
306         EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
307
308         genlist_missed = elm_genlist_add(obj);
309         EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
310
311         itc = elm_genlist_item_class_new();
312         EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
313         itc->item_style = "history";
314         itc->func.text_get = _item_label_get;
315         itc->func.content_get = _item_content_get;
316         itc->func.state_get = _item_state_get;
317         itc->func.del = NULL;
318         history->genlist_all = genlist_all;
319         history->genlist_missed = genlist_missed;
320         history->itc = itc;
321
322         elm_object_part_content_set(obj, "elm.swallow.all", genlist_all);
323         elm_object_part_content_set(obj, "elm.swallow.missed", genlist_missed);
324         elm_object_signal_emit(obj, "show,all", "gui");
325         elm_object_signal_callback_add(obj, "clicked,*", "gui",
326                                         _on_clicked, NULL);
327
328         config_path = efreet_config_home_get();
329         snprintf(path, sizeof(path), "%s/%s", config_path, PACKAGE_NAME);
330         ecore_file_mkpath(path);
331         snprintf(path, sizeof(path), "%s/%s/history.eet", config_path,
332                         PACKAGE_NAME);
333         history->log = eet_open(path, EET_FILE_MODE_READ_WRITE);
334         EINA_SAFETY_ON_NULL_GOTO(history->log, err_item_class);
335
336         _history_call_info_descriptor_init(&history->edd, &history->edd_list);
337         _history_call_log_read(history);
338         EINA_SAFETY_ON_NULL_GOTO(history->calls, err_log_read);
339         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
340                                         history);
341         callback_node_call_changed =
342                 ofono_call_changed_cb_add(_history_call_changed, history);
343         callback_node_call_removed =
344                 ofono_call_removed_cb_add(_history_call_removed, history);
345         return obj;
346
347 err_log_read:
348         eet_close(history->log);
349 err_item_class:
350         elm_genlist_item_class_free(itc);
351 err_object_new:
352         free(obj);
353 err_layout:
354         free(history);
355         eet_shutdown();
356         return NULL;
357 }