4 #include <Elementary.h>
16 #ifndef EET_COMPRESSION_DEFAULT
17 #define EET_COMPRESSION_DEFAULT 1
20 #define HISTORY_ENTRY "history"
22 typedef struct _Call_Info_List {
27 typedef struct _History {
29 Eet_Data_Descriptor *edd;
30 Eet_Data_Descriptor *edd_list;
31 Call_Info_List *calls;
32 Elm_Genlist_Item_Class *itc;
33 Evas_Object *genlist_all, *genlist_missed;
34 Ecore_Poller *updater;
37 typedef struct _Call_Info {
40 long long creation_time; /* not in edd */
45 const OFono_Call *call; /* not in edd */
46 Elm_Object_Item *it_all; /*not in edd */
47 Elm_Object_Item *it_missed; /*not in edd */
50 static OFono_Callback_List_Call_Node *callback_node_call_removed = NULL;
51 static OFono_Callback_List_Call_Node *callback_node_call_changed = NULL;
53 static Eina_Bool _history_time_updater(void *data)
57 long long update_threshold = time(NULL) - WEEK - DAY;
59 it = elm_genlist_first_item_get(ctx->genlist_all);
60 for (; it != NULL; it = elm_genlist_item_next_get(it)) {
61 const Call_Info *call_info = elm_object_item_data_get(it);
62 if (call_info->start_time < update_threshold)
64 elm_genlist_item_update(it);
67 it = elm_genlist_first_item_get(ctx->genlist_missed);
68 for (; it != NULL; it = elm_genlist_item_next_get(it)) {
69 const Call_Info *call_info = elm_object_item_data_get(it);
70 if (call_info->start_time < update_threshold)
72 elm_genlist_item_update(it);
78 static Call_Info *_history_call_info_search(const History *history,
79 const OFono_Call *call)
83 long long t = ofono_call_full_start_time_get(call);
84 const char *line_id = ofono_call_line_id_get(call); /* stringshare */
86 EINA_LIST_FOREACH(history->calls->list, l, call_info) {
87 if (call_info->call == call)
89 else if (!call_info->call) {
90 if ((t > 0) && (call_info->start_time == t) &&
91 (line_id == call_info->line_id)) {
92 DBG("associated existing log %p %s (%lld) with "
96 call_info->start_time,
98 call_info->call = call;
107 static Eina_Bool _history_call_info_update(Call_Info *call_info)
109 OFono_Call_State state;
111 EINA_SAFETY_ON_NULL_RETURN_VAL(call_info->call, EINA_FALSE);
112 state = ofono_call_state_get(call_info->call);
114 if (state == OFONO_CALL_STATE_INCOMING ||
115 state == OFONO_CALL_STATE_WAITING) {
116 if (!call_info->incoming) {
117 call_info->incoming = EINA_TRUE;
120 } else if (state == OFONO_CALL_STATE_DIALING ||
121 state == OFONO_CALL_STATE_ALERTING) {
122 if (!call_info->incoming) {
123 call_info->incoming = EINA_FALSE;
126 } else if (state == OFONO_CALL_STATE_ACTIVE ||
127 state == OFONO_CALL_STATE_HELD) {
128 if (!call_info->completed) {
129 call_info->start_time = ofono_call_full_start_time_get
131 if (call_info->start_time == 0)
132 call_info->start_time = call_info->creation_time;
134 call_info->completed = EINA_TRUE;
142 static void _dial_reply(void *data, OFono_Error err,
143 OFono_Call *call __UNUSED__)
145 const char *number = data;
147 if (err != OFONO_ERROR_NONE) {
149 snprintf(buf, sizeof(buf), "Could not call: %s", number);
150 gui_simple_popup("Error", buf);
154 static void _on_item_clicked(void *data, Evas_Object *obj __UNUSED__,
157 Elm_Object_Item *it = event_info;
158 const char *number = data;
160 INF("call %s", number);
161 ofono_dial(number, NULL, _dial_reply, number);
162 elm_genlist_item_selected_set(it, EINA_FALSE);
165 static void _history_call_changed(void *data, OFono_Call *call)
167 History *history = data;
168 const char *line_id = ofono_call_line_id_get(call);
169 Call_Info *call_info;
170 OFono_Call_State state = ofono_call_state_get(call);
172 call_info = _history_call_info_search(history, call);
173 DBG("call=%p, id=%s, state=%d, completed=%d, incoming=%d, info=%p",
174 call, line_id, state,
175 call_info ? call_info->completed : EINA_FALSE,
176 call_info ? call_info->incoming : EINA_FALSE,
182 call_info = calloc(1, sizeof(Call_Info));
183 EINA_SAFETY_ON_NULL_RETURN(call_info);
185 call_info->call = call;
186 call_info->start_time = ofono_call_full_start_time_get(call);
187 call_info->creation_time = time(NULL);
188 call_info->line_id = eina_stringshare_add(line_id);
189 call_info->name = eina_stringshare_add(ofono_call_name_get(call));
190 history->calls->list =
191 eina_list_prepend(history->calls->list, call_info);
192 history->calls->dirty = EINA_TRUE;
195 if (_history_call_info_update(call_info))
196 history->calls->dirty = EINA_TRUE;
199 static void _history_call_log_save(History *history)
203 EINA_SAFETY_ON_NULL_RETURN(history->calls);
204 DBG("save history (%u calls, dirty: %d) to %s",
205 eina_list_count(history->calls->list), history->calls->dirty,
208 ecore_file_unlink(history->bkp);
209 ecore_file_mv(history->path, history->bkp);
210 efile = eet_open(history->path, EET_FILE_MODE_WRITE);
211 EINA_SAFETY_ON_NULL_RETURN(efile);
212 if (!(eet_data_write(efile,
213 history->edd_list, HISTORY_ENTRY,
214 history->calls, EET_COMPRESSION_DEFAULT)))
215 ERR("Could in the history log file");
220 static void _history_call_removed(void *data, OFono_Call *call)
223 History *history = data;
224 const char *line_id = ofono_call_line_id_get(call);
225 Call_Info *call_info;
229 call_info = _history_call_info_search(history, call);
230 DBG("call=%p, id=%s, info=%p", call, line_id, call_info);
231 EINA_SAFETY_ON_NULL_RETURN(call_info);
233 if (call_info->start_time == 0)
234 call_info->start_time = call_info->creation_time;
236 start = call_info->start_time;
239 call_info->end_time = time(NULL);
240 call_info->call = NULL;
242 if (call_info->completed)
243 INF("Call end: %s at %s", line_id, tm);
245 if (!call_info->incoming)
246 INF("Not answered: %s at %s", line_id, tm);
248 INF("Missed: %s at %s", line_id, tm);
249 if (call_info->it_missed)
250 elm_genlist_item_update(call_info->it_missed);
252 it = elm_genlist_item_prepend
253 (history->genlist_missed,
256 ELM_GENLIST_ITEM_NONE,
259 elm_genlist_item_show
260 (it, ELM_GENLIST_ITEM_SCROLLTO_IN);
261 call_info->it_missed = it;
266 history->calls->dirty = EINA_TRUE;
267 _history_call_log_save(history);
269 if (call_info->it_all)
270 elm_genlist_item_update(call_info->it_all);
272 it = elm_genlist_item_prepend(history->genlist_all,
275 ELM_GENLIST_ITEM_NONE,
278 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN);
279 call_info->it_all = it;
283 static void _call_info_free(Call_Info *call_info)
285 eina_stringshare_del(call_info->line_id);
286 eina_stringshare_del(call_info->name);
290 static void _on_del(void *data, Evas *e __UNUSED__,
291 Evas_Object *obj __UNUSED__, void *event __UNUSED__)
293 History *history = data;
294 Call_Info *call_info;
296 ecore_poller_del(history->updater);
298 if (history->calls->dirty)
299 _history_call_log_save(history);
301 ofono_call_removed_cb_del(callback_node_call_removed);
302 ofono_call_changed_cb_del(callback_node_call_changed);
303 eet_data_descriptor_free(history->edd);
304 eet_data_descriptor_free(history->edd_list);
305 EINA_LIST_FREE(history->calls->list, call_info)
306 _call_info_free(call_info);
307 free(history->calls);
308 elm_genlist_item_class_free(history->itc);
312 ecore_file_shutdown();
316 static void _history_call_info_descriptor_init(Eet_Data_Descriptor **edd,
317 Eet_Data_Descriptor **edd_list)
319 Eet_Data_Descriptor_Class eddc;
321 EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info);
322 *edd = eet_data_descriptor_stream_new(&eddc);
324 EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info_List);
325 *edd_list = eet_data_descriptor_stream_new(&eddc);
327 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
328 "completed", completed, EET_T_UCHAR);
329 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
330 "incoming", incoming, EET_T_UCHAR);
332 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
333 "start_time", start_time, EET_T_LONG_LONG);
334 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
335 "end_time", end_time, EET_T_LONG_LONG);
336 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
337 "line_id", line_id, EET_T_STRING);
338 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
339 "name", name, EET_T_STRING);
341 EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Call_Info_List, "list", list,
345 static void _history_call_log_read(History *history)
347 Call_Info *call_info;
350 Call_Info_List *calls = NULL;
353 efile = eet_open(history->path, EET_FILE_MODE_READ);
356 calls = eet_data_read(efile, history->edd_list, HISTORY_ENTRY);
361 efile = eet_open(history->bkp, EET_FILE_MODE_READ);
363 calls = eet_data_read(efile, history->edd_list,
370 calls = calloc(1, sizeof(Call_Info_List));
372 history->calls = calls;
373 EINA_SAFETY_ON_NULL_RETURN(history->calls);
375 EINA_LIST_FOREACH(history->calls->list, l, call_info) {
376 it = elm_genlist_item_append(history->genlist_all,
379 ELM_GENLIST_ITEM_NONE,
382 call_info->it_all = it;
384 if (call_info->completed)
387 it = elm_genlist_item_append(history->genlist_missed,
388 history->itc, call_info, NULL,
389 ELM_GENLIST_ITEM_NONE,
392 call_info->it_missed = it;
395 it = elm_genlist_first_item_get(history->genlist_all);
397 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
399 it = elm_genlist_first_item_get(history->genlist_missed);
401 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
404 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
407 Call_Info *call_info = data;
409 if (strncmp(part, "text.call", strlen("text.call")))
412 part += strlen("text.call.");
414 if (!strcmp(part, "name")) {
415 if (!call_info->name || call_info->name[0] == '\0')
416 return phone_format(call_info->line_id);
417 return strdup(call_info->name);
420 if (!strcmp(part, "time")) {
421 if ((call_info->completed) && (call_info->end_time))
422 return date_format(call_info->end_time);
423 return date_format(call_info->start_time);
426 /* TODO: Fetch phone type from contacts information*/
427 if (!strcmp(part, "type"))
428 return strdup("TODO:TELEPHONE TYPE");
430 ERR("Unexpected text part: %s", part);
435 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
438 Call_Info *call_info = data;
440 if (!strcmp(part, "missed"))
441 return !call_info->completed;
442 else if (!strcmp(part, "completed"))
443 return call_info->completed;
444 else if (!strcmp(part, "outgoing"))
445 return !call_info->incoming;
446 else if (!strcmp(part, "incoming"))
447 return call_info->incoming;
449 ERR("Unexpected state part: %s", part);
453 static void _on_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
454 const char *emission, const char *source __UNUSED__)
456 EINA_SAFETY_ON_NULL_RETURN(emission);
457 emission += strlen("clicked,");
459 if (!strcmp(emission, "all"))
460 elm_object_signal_emit(obj, "show,all", "gui");
462 elm_object_signal_emit(obj, "show,missed", "gui");
465 static void _on_more_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
466 const char *emission __UNUSED__,
467 const char *source __UNUSED__)
472 static Evas_Object *_item_content_get(void *data __UNUSED__, Evas_Object *obj,
473 const char *part __UNUSED__)
477 btn = gui_layout_add(obj, "history/img");
478 EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
479 elm_object_signal_callback_add(btn, "clicked,more", "gui",
480 _on_more_clicked, NULL);
481 evas_object_propagate_events_set(btn, EINA_FALSE);
486 Evas_Object *history_add(Evas_Object *parent)
490 const char *config_path;
491 char *path, base_dir[PATH_MAX];
492 Elm_Genlist_Item_Class *itc;
493 Evas_Object *obj, *genlist_all, *genlist_missed;
497 history = calloc(1, sizeof(History));
498 EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
500 obj = gui_layout_add(parent, "history_bg");
501 EINA_SAFETY_ON_NULL_GOTO(obj, err_layout);
503 genlist_all = elm_genlist_add(obj);
504 EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
505 elm_object_style_set(genlist_all, "history");
507 genlist_missed = elm_genlist_add(obj);
508 EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
509 elm_object_style_set(genlist_missed, "history");
511 itc = elm_genlist_item_class_new();
512 EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
513 itc->item_style = "history";
514 itc->func.text_get = _item_label_get;
515 itc->func.content_get = _item_content_get;
516 itc->func.state_get = _item_state_get;
517 itc->func.del = NULL;
518 history->genlist_all = genlist_all;
519 history->genlist_missed = genlist_missed;
522 elm_object_part_content_set(obj, "elm.swallow.all", genlist_all);
523 elm_object_part_content_set(obj, "elm.swallow.missed", genlist_missed);
524 elm_object_signal_emit(obj, "show,all", "gui");
525 elm_object_signal_callback_add(obj, "clicked,*", "gui",
528 config_path = efreet_config_home_get();
529 snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
531 ecore_file_mkpath(base_dir);
532 r = asprintf(&path, "%s/%s/history.eet", config_path, PACKAGE_NAME);
537 history->path = path;
538 r = asprintf(&path, "%s/%s/history.eet.bkp", config_path,
546 _history_call_info_descriptor_init(&history->edd, &history->edd_list);
547 _history_call_log_read(history);
548 EINA_SAFETY_ON_NULL_GOTO(history->calls, err_log_read);
549 evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
551 callback_node_call_changed =
552 ofono_call_changed_cb_add(_history_call_changed, history);
553 callback_node_call_removed =
554 ofono_call_removed_cb_add(_history_call_removed, history);
556 /* ECORE_POLLER_CORE is 1/8th of second. */
557 history->updater = ecore_poller_add(ECORE_POLLER_CORE, 8 * 60,
558 _history_time_updater,
564 eet_data_descriptor_free(history->edd);
565 eet_data_descriptor_free(history->edd_list);
569 elm_genlist_item_class_free(itc);
574 ecore_file_shutdown();