Changing the way that we store the history log.
[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         char *path, *bkp;
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         Eet_File *efile;
89
90         ecore_file_unlink(history->bkp);
91         ecore_file_mv(history->path, history->bkp);
92         efile = eet_open(history->path, EET_FILE_MODE_WRITE);
93         EINA_SAFETY_ON_NULL_RETURN(efile);
94         if (!(eet_data_write(efile,
95                                 history->edd_list, HISTORY_ENTRY,
96                                 history->calls, EET_COMPRESSION_DEFAULT)))
97                 ERR("Could in the history log file");
98
99         eet_close(efile);
100 }
101
102 static void _history_call_removed(void *data, OFono_Call *call)
103 {
104         History *history = data;
105         const char *line_id = ofono_call_line_id_get(call);
106         Call_Info *call_info;
107         time_t start;
108         char *tm;
109
110         call_info =
111                 _history_call_info_list_search(history->calls->list, line_id);
112         EINA_SAFETY_ON_NULL_RETURN(call_info);
113         start = call_info->start_time;
114         tm = ctime(&start);
115
116         if (call_info->state == OFONO_CALL_STATE_INCOMING) {
117                 INF("Missed call - Id: %s - time: %s", line_id, tm);
118                 elm_genlist_item_append(history->genlist_missed, history->itc,
119                                         call_info, NULL, ELM_GENLIST_ITEM_NONE,
120                                         NULL, NULL);
121         } else if (call_info->state == OFONO_CALL_STATE_DIALING)
122                 INF("Call not answered - Id: %s - time: %s", line_id, tm);
123         else
124                 INF("A call has ended - Id: %s - time: %s", line_id, tm);
125
126         call_info->end_time = time(NULL);
127         _history_call_log_save(history);
128         elm_genlist_item_append(history->genlist_all, history->itc, call_info,
129                                 NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
130 }
131
132 static void _call_info_free(Call_Info *call_info)
133 {
134         eina_stringshare_del(call_info->line_id);
135         eina_stringshare_del(call_info->name);
136         free(call_info);
137 }
138
139 static void _on_del(void *data, Evas *e __UNUSED__,
140                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
141 {
142         History *history = data;
143         Call_Info *call_info;
144         ofono_call_removed_cb_del(callback_node_call_removed);
145         ofono_call_changed_cb_del(callback_node_call_changed);
146         eet_data_descriptor_free(history->edd);
147         eet_data_descriptor_free(history->edd_list);
148         EINA_LIST_FREE(history->calls->list, call_info)
149                 _call_info_free(call_info);
150         free(history->calls);
151         elm_genlist_item_class_free(history->itc);
152         free(history->path);
153         free(history->bkp);
154         free(history);
155         ecore_file_shutdown();
156         eet_shutdown();
157 }
158
159 static void _history_call_info_descriptor_init(Eet_Data_Descriptor **edd,
160                                                 Eet_Data_Descriptor **edd_list)
161 {
162         Eet_Data_Descriptor_Class eddc;
163
164         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info);
165         *edd = eet_data_descriptor_stream_new(&eddc);
166
167         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info_List);
168         *edd_list = eet_data_descriptor_stream_new(&eddc);
169
170         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
171                                         "state", state, EET_T_INT);
172         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
173                                         "start_time", start_time, EET_T_LONG_LONG);
174         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
175                                         "end_time", end_time, EET_T_LONG_LONG);
176         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
177                                         "line_id", line_id, EET_T_STRING);
178         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
179                                         "name", name, EET_T_STRING);
180
181         EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Call_Info_List, "list", list,
182                                         *edd);
183 }
184
185 static void _history_call_log_read(History *history)
186 {
187         Call_Info *call_info;
188         Eina_List *l;
189         Eet_File *efile;
190         Call_Info_List *calls = NULL;
191
192         efile = eet_open(history->path, EET_FILE_MODE_READ);
193
194         if (efile) {
195                 calls = eet_data_read(efile, history->edd_list, HISTORY_ENTRY);
196                 eet_close(efile);
197         }
198
199         if (!calls) {
200                 efile = eet_open(history->bkp, EET_FILE_MODE_READ);
201                 if (efile) {
202                         calls = eet_data_read(efile, history->edd_list,
203                                                 HISTORY_ENTRY);
204                         eet_close(efile);
205                 }
206         }
207
208         if (!calls)
209                 history->calls = calloc(1, sizeof(Call_Info_List));
210
211         history->calls = calls;
212         EINA_SAFETY_ON_NULL_RETURN(history->calls);
213
214         EINA_LIST_FOREACH(history->calls->list, l, call_info) {
215                 if (!call_info)
216                         continue;
217                 elm_genlist_item_append(history->genlist_all, history->itc,
218                                         call_info, NULL, ELM_GENLIST_ITEM_NONE,
219                                         NULL, NULL);
220                 if (call_info->state == OFONO_CALL_STATE_INCOMING)
221                         elm_genlist_item_append(history->genlist_missed,
222                                                 history->itc, call_info, NULL,
223                                                 ELM_GENLIST_ITEM_NONE,
224                                                 NULL, NULL);
225         }
226 }
227
228 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
229                                 const char *part)
230 {
231         Call_Info *call_info = data;
232
233         if (strncmp(part, "text.call", strlen("text.call")))
234                 return NULL;
235
236         part += strlen("text.call.");
237
238         if (!strcmp(part, "name")) {
239                 const char *name;
240                 name = call_info->name;
241                 if (!call_info->name || call_info->name[0] == '\0')
242                         name = call_info->line_id;
243                 return strdup(name);
244         }
245
246         if (!strcmp(part, "time"))
247                 return date_format(call_info->end_time);
248
249         /* TODO: Fetch phone type from contacts information*/
250         if (!strcmp(part, "type"))
251                 return strdup("TODO:TELEPHONE TYPE");
252
253         ERR("Unexpected text part: %s", part);
254         return NULL;
255 }
256
257
258 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
259                                         const char  *part)
260 {
261         Call_Info *call_info = data;
262
263         if (!strcmp(part, "missed")) {
264                 if (call_info->state == OFONO_CALL_STATE_INCOMING ||
265                         call_info->state == OFONO_CALL_STATE_DIALING)
266                         return EINA_TRUE;
267                 else
268                         return EINA_FALSE;
269         } else if (!strcmp(part, "completed")) {
270                 if (call_info->state == OFONO_CALL_STATE_INCOMING ||
271                         call_info->state == OFONO_CALL_STATE_DIALING)
272                         return EINA_FALSE;
273                 else
274                         return EINA_TRUE;
275         } else if (!strcmp(part, "outgoing"))
276                 return EINA_TRUE;
277         else if (!strcmp(part, "incoming"))
278                 return EINA_TRUE;
279
280         ERR("Unexpected state part: %s", part);
281         return EINA_FALSE;
282 }
283
284 static void _on_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
285                         const char *emission, const char *source __UNUSED__)
286 {
287         EINA_SAFETY_ON_NULL_RETURN(emission);
288         emission += strlen("clicked,");
289
290         if (!strcmp(emission, "all"))
291                 elm_object_signal_emit(obj, "show,all", "gui");
292         else
293                 elm_object_signal_emit(obj, "show,missed", "gui");
294 }
295
296 static void _on_more_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
297                                 const char *emission __UNUSED__,
298                                 const char *source __UNUSED__)
299 {
300         DBG("TODO");
301 }
302
303 static Evas_Object *_item_content_get(void *data __UNUSED__, Evas_Object *obj,
304                                         const char *part __UNUSED__)
305 {
306         Evas_Object *btn;
307
308         btn = gui_layout_add(obj, "history/img");
309         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
310         elm_object_signal_callback_add(btn, "clicked,more", "gui",
311                                         _on_more_clicked, NULL);
312
313         return btn;
314 }
315
316 Evas_Object *history_add(Evas_Object *parent)
317 {
318         int r;
319         History *history;
320         const char *config_path;
321         char *path, base_dir[PATH_MAX];
322         Elm_Genlist_Item_Class *itc;
323         Evas_Object *obj, *genlist_all, *genlist_missed;
324
325         eet_init();
326         ecore_file_init();
327         history = calloc(1, sizeof(History));
328         EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
329
330         obj = gui_layout_add(parent, "history_bg");
331         EINA_SAFETY_ON_NULL_GOTO(obj, err_layout);
332
333         genlist_all = elm_genlist_add(obj);
334         EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
335
336         genlist_missed = elm_genlist_add(obj);
337         EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
338
339         itc = elm_genlist_item_class_new();
340         EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
341         itc->item_style = "history";
342         itc->func.text_get = _item_label_get;
343         itc->func.content_get = _item_content_get;
344         itc->func.state_get = _item_state_get;
345         itc->func.del = NULL;
346         history->genlist_all = genlist_all;
347         history->genlist_missed = genlist_missed;
348         history->itc = itc;
349
350         elm_object_part_content_set(obj, "elm.swallow.all", genlist_all);
351         elm_object_part_content_set(obj, "elm.swallow.missed", genlist_missed);
352         elm_object_signal_emit(obj, "show,all", "gui");
353         elm_object_signal_callback_add(obj, "clicked,*", "gui",
354                                         _on_clicked, NULL);
355
356         config_path = efreet_config_home_get();
357         snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
358                         PACKAGE_NAME);
359         ecore_file_mkpath(base_dir);
360         r = asprintf(&path,  "%s/%s/history.eet", config_path, PACKAGE_NAME);
361
362         if (r < 0)
363                 goto err_item_class;
364
365         history->path = path;
366         r = asprintf(&path,  "%s/%s/history.eet.bkp", config_path,
367                         PACKAGE_NAME);
368
369         if (r < 0)
370                 goto err_path;
371
372         history->bkp = path;
373
374         _history_call_info_descriptor_init(&history->edd, &history->edd_list);
375         _history_call_log_read(history);
376         EINA_SAFETY_ON_NULL_GOTO(history->calls, err_log_read);
377         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
378                                         history);
379         callback_node_call_changed =
380                 ofono_call_changed_cb_add(_history_call_changed, history);
381         callback_node_call_removed =
382                 ofono_call_removed_cb_add(_history_call_removed, history);
383         return obj;
384
385 err_log_read:
386         free(history->bkp);
387         eet_data_descriptor_free(history->edd);
388         eet_data_descriptor_free(history->edd_list);
389 err_path:
390         free(history->path);
391 err_item_class:
392         elm_genlist_item_class_free(itc);
393 err_object_new:
394         free(obj);
395 err_layout:
396         free(history);
397         ecore_file_shutdown();
398         eet_shutdown();
399         return NULL;
400 }