history: be quiet if there was no previous 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
15 #define HISTORY_ENTRY "history"
16
17 typedef struct _Call_Info_List {
18         Eina_List *list;
19 } Call_Info_List;
20
21 typedef struct _History {
22         Eet_File *log;
23         Eet_Data_Descriptor *edd;
24         Eet_Data_Descriptor *edd_list;
25         Call_Info_List *calls;
26         Elm_Genlist_Item_Class *itc;
27         Evas_Object *genlist_all, *genlist_missed;
28         Elm_Object_Item *all, *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         Call_Info *call_info;
45         Eina_List *l;
46
47         EINA_LIST_FOREACH (list, l, call_info)
48                 if (!strcmp(call_info->line_id, line_id))
49                         return call_info;
50
51         return NULL;
52 }
53
54 static void _history_call_changed(void *data, OFono_Call *call) {
55         History *history = data;
56         const char *line_id = ofono_call_line_id_get(call);
57         Call_Info *call_info;
58         OFono_Call_State state = ofono_call_state_get(call);
59
60         call_info =
61                 _history_call_info_list_search(history->calls->list, line_id);
62
63         if (call_info) {
64                 /* Otherwise I missed the call or the person didn't
65                  * awnser my call!
66                  */
67                 if ((call_info->state == OFONO_CALL_STATE_INCOMING ||
68                         call_info->state == OFONO_CALL_STATE_DIALING) &&
69                         state != OFONO_CALL_STATE_DISCONNECTED)
70                         call_info->state = state;
71                 return;
72         }
73         call_info = calloc(1, sizeof(Call_Info));
74         EINA_SAFETY_ON_NULL_RETURN(call_info);
75
76         call_info->state = state;
77         call_info->start_time = time(NULL);
78         call_info->line_id = eina_stringshare_add(line_id);
79         call_info->name = eina_stringshare_add(ofono_call_name_get(call));
80         history->calls->list =
81                 eina_list_prepend(history->calls->list, call_info);
82 }
83
84 static void _history_call_log_save(History *history) {
85         if (!(eet_data_write(history->log,
86                                 history->edd_list, HISTORY_ENTRY,
87                                 history->calls, EET_COMPRESSION_DEFAULT)))
88                 ERR("Could in the history log file");
89 }
90
91 static void _history_call_removed(void *data, OFono_Call *call) {
92         History *history = data;
93         const char *line_id = ofono_call_line_id_get(call);
94         Call_Info *call_info;
95         time_t start;
96         char *tm;
97
98         call_info =
99                 _history_call_info_list_search(history->calls->list, line_id);
100         EINA_SAFETY_ON_NULL_RETURN(call_info);
101         start = call_info->start_time;
102         tm = ctime(&start);
103
104         if (call_info->state == OFONO_CALL_STATE_INCOMING) {
105                 INF("Missed call - Id: %s - time: %s", line_id, tm);
106                 elm_genlist_item_append(history->genlist_missed, history->itc,
107                                         call_info, NULL, ELM_GENLIST_ITEM_NONE,
108                                         NULL, NULL);
109         } else if (call_info->state == OFONO_CALL_STATE_DIALING)
110                 INF("Call not answered - Id: %s - time: %s", line_id, tm);
111         else
112                 INF("A call has ended - Id: %s - time: %s", line_id, tm);
113
114         call_info->end_time = time(NULL);
115         _history_call_log_save(history);
116         elm_genlist_item_append(history->genlist_all, history->itc, call_info,
117                                 NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
118 }
119
120 static void _call_info_free(Call_Info *call_info) {
121         eina_stringshare_del(call_info->line_id);
122         eina_stringshare_del(call_info->name);
123         free(call_info);
124 }
125
126 static void _on_del(void *data, Evas *e __UNUSED__,
127                         Evas_Object *obj __UNUSED__, void *event __UNUSED__) {
128         History *history = data;
129         Call_Info *call_info;
130         ofono_call_removed_cb_del(callback_node_call_removed);
131         ofono_call_changed_cb_del(callback_node_call_changed);
132         eet_close(history->log);
133         eet_data_descriptor_free(history->edd);
134         eet_data_descriptor_free(history->edd_list);
135         EINA_LIST_FREE(history->calls->list, call_info) {
136                 _call_info_free(call_info);
137         }
138         free(history->calls);
139         elm_genlist_item_class_free(history->itc);
140         free(history);
141         eet_shutdown();
142 }
143
144 static void _history_call_info_descriptor_init(Eet_Data_Descriptor **edd,
145                                                 Eet_Data_Descriptor **edd_list) {
146         Eet_Data_Descriptor_Class eddc;
147
148         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info);
149         *edd = eet_data_descriptor_stream_new(&eddc);
150
151         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info_List);
152         *edd_list = eet_data_descriptor_stream_new(&eddc);
153
154         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
155                                         "state", state, EET_T_INT);
156         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
157                                         "start_time", start_time, EET_T_LONG_LONG);
158         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
159                                         "end_time", end_time, EET_T_LONG_LONG);
160         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
161                                         "line_id", line_id, EET_T_STRING);
162         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
163                                         "name", name, EET_T_STRING);
164
165         EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Call_Info_List, "list", list,
166                                         *edd);
167 }
168
169 static void _history_call_log_read(History *history) {
170         Call_Info *call_info;
171         Eina_List *l;
172
173         history->calls = eet_data_read(history->log, history->edd_list,
174                                                         HISTORY_ENTRY);
175
176         if (!history->calls) {
177                 history->calls = calloc(1, sizeof(Call_Info_List));
178                 return;
179         }
180
181         EINA_LIST_FOREACH (history->calls->list, l, call_info) {
182                 if (!call_info)
183                         continue;
184                 elm_genlist_item_append(history->genlist_all, history->itc,
185                                         call_info, NULL, ELM_GENLIST_ITEM_NONE,
186                                         NULL, NULL);
187                 if (call_info->state == OFONO_CALL_STATE_INCOMING)
188                         elm_genlist_item_append(history->genlist_missed,
189                                                 history->itc, call_info, NULL,
190                                                 ELM_GENLIST_ITEM_NONE,
191                                                 NULL, NULL);
192         }
193 }
194
195 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
196                                 const char *part __UNUSED__) {
197         Call_Info *call_info = data;
198         char *buf;
199         const char *name, *call_state;
200         char *t;
201
202         name = call_info->name;
203
204         if (!call_info->name || call_info->name[0] == '\0')
205                 name = call_info->line_id;
206
207         if (call_info->state == OFONO_CALL_STATE_INCOMING)
208                 call_state = "Missed";
209         else if (call_info->state == OFONO_CALL_STATE_DIALING)
210                 call_state = "Not Awnsered";
211         else
212                 call_state = "Completed";
213
214         t = date_format(call_info->end_time);
215
216         if (asprintf(&buf, "%s-%s-%s", name, call_state, t) < 0)
217                 buf = strdup("");
218
219         free(t);
220         return buf;
221 }
222
223 static void _btn_naviframe_next_click(void *data, Evas_Object *obj __UNUSED__,
224                                         void *event_inf __UNUSED__) {
225         History *history = data;
226         elm_naviframe_item_promote(history->missed);
227 }
228
229 static void _btn_naviframe_prev_click(void *data, Evas_Object *obj __UNUSED__,
230                                         void *event_inf __UNUSED__) {
231         History *history = data;
232         elm_naviframe_item_promote(history->all);
233 }
234
235 Evas_Object *history_add(Evas_Object *parent) {
236         History *history;
237         const char *config_path;
238         char path[PATH_MAX];
239         Elm_Genlist_Item_Class *itc;
240         Evas_Object *obj, *genlist_all, *genlist_missed, *btn;
241
242         eet_init();
243         history = calloc(1, sizeof(History));
244         EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
245
246         obj = elm_naviframe_add(parent);
247         EINA_SAFETY_ON_NULL_GOTO(obj, err_naviframe);
248         elm_naviframe_prev_btn_auto_pushed_set(obj, EINA_FALSE);
249
250         genlist_all = elm_genlist_add(obj);
251         EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
252
253         genlist_missed = elm_genlist_add(obj);
254         EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
255
256         itc = elm_genlist_item_class_new();
257         EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
258         itc->item_style = "default";
259         itc->func.text_get = _item_label_get;
260         itc->func.content_get = NULL;
261         itc->func.state_get = NULL;
262         itc->func.del = NULL;
263         history->genlist_all = genlist_all;
264         history->genlist_missed = genlist_missed;
265
266         btn = elm_button_add(obj);
267         EINA_SAFETY_ON_NULL_GOTO(btn, err_item_class);
268         elm_object_text_set(btn, "Missed");
269         evas_object_smart_callback_add(btn, "clicked",
270                                         _btn_naviframe_next_click, history);
271
272         Elm_Object_Item *all =
273                 elm_naviframe_item_push(obj, "All", NULL, btn, genlist_all,
274                                         NULL);
275         EINA_SAFETY_ON_NULL_GOTO(all, err_item_class);
276
277         btn = elm_button_add(obj);
278         EINA_SAFETY_ON_NULL_GOTO(btn, err_item_class);
279         elm_object_text_set(btn, "All");
280         evas_object_smart_callback_add(btn, "clicked",
281                                         _btn_naviframe_prev_click, history);
282
283         Elm_Object_Item *missed =
284                 elm_naviframe_item_push(obj, "Missed", btn, NULL,
285                                         genlist_missed, NULL);
286         EINA_SAFETY_ON_NULL_GOTO(missed, err_item_class);
287         elm_naviframe_item_promote(all);
288
289         history->all = all;
290         history->missed = missed;
291         history->itc = itc;
292
293         config_path = efreet_config_home_get();
294         snprintf(path, sizeof(path), "%s%s", config_path, PACKAGE_NAME);
295         ecore_file_mkpath(path);
296         snprintf(path, sizeof(path), "%s%s/history.eet", config_path,
297                         PACKAGE_NAME);
298         history->log = eet_open(path, EET_FILE_MODE_READ_WRITE);
299         EINA_SAFETY_ON_NULL_RETURN_VAL(history->log, NULL);
300
301         _history_call_info_descriptor_init(&history->edd, &history->edd_list);
302         _history_call_log_read(history);
303         EINA_SAFETY_ON_NULL_RETURN_VAL(history->calls, NULL);
304         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
305                                         history);
306         callback_node_call_changed =
307                 ofono_call_changed_cb_add(_history_call_changed, history);
308         callback_node_call_removed =
309                 ofono_call_removed_cb_add(_history_call_removed, history);
310         return obj;
311
312 err_item_class:
313         elm_genlist_item_class_free(itc);
314 err_object_new:
315         free(obj);
316 err_naviframe:
317         free(history);
318         return NULL;
319 }