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;
34 Evas_Object *genlist_all, *genlist_missed;
35 Ecore_Poller *updater;
39 typedef struct _Call_Info {
42 long long creation_time; /* not in edd */
47 const OFono_Call *call; /* not in edd */
48 Elm_Object_Item *it_all; /*not in edd */
49 Elm_Object_Item *it_missed; /*not in edd */
52 static OFono_Callback_List_Call_Node *callback_node_call_removed = NULL;
53 static OFono_Callback_List_Call_Node *callback_node_call_changed = NULL;
55 static Eina_Bool _history_time_updater(void *data)
59 long long update_threshold = time(NULL) - WEEK - DAY;
60 double now = ecore_loop_time_get();
62 if (now - ctx->last_update < 1.0)
64 ctx->last_update = now;
66 it = elm_genlist_first_item_get(ctx->genlist_all);
67 for (; it != NULL; it = elm_genlist_item_next_get(it)) {
68 const Call_Info *call_info = elm_object_item_data_get(it);
69 if (call_info->start_time < update_threshold)
71 elm_genlist_item_update(it);
74 it = elm_genlist_first_item_get(ctx->genlist_missed);
75 for (; it != NULL; it = elm_genlist_item_next_get(it)) {
76 const Call_Info *call_info = elm_object_item_data_get(it);
77 if (call_info->start_time < update_threshold)
79 elm_genlist_item_update(it);
85 static void _history_time_updater_stop(History *history)
87 Evas *e = evas_object_evas_get(history->self);
88 Eina_Bool win_focused = evas_focus_state_get(e);
89 Eina_Bool obj_visible = evas_object_visible_get(history->self);
91 DBG("poller %p, win_focused=%hhu, obj_visible=%hhu",
92 history->updater, win_focused, obj_visible);
93 if (!history->updater)
95 if (win_focused && obj_visible)
98 DBG("delete poller %p", history->updater);
99 ecore_poller_del(history->updater);
100 history->updater = NULL;
103 static void _history_time_updater_start(History *history)
105 Evas *e = evas_object_evas_get(history->self);
106 Eina_Bool win_focused = evas_focus_state_get(e);
107 Eina_Bool obj_visible = evas_object_visible_get(history->self);
109 DBG("poller %p, win_focused=%hhu, obj_visible=%hhu",
110 history->updater, win_focused, obj_visible);
111 if (history->updater)
113 if ((!win_focused) || (!obj_visible))
117 /* ECORE_POLLER_CORE is 1/8th of second. */
118 history->updater = ecore_poller_add(ECORE_POLLER_CORE, 8 * 60,
119 _history_time_updater,
121 _history_time_updater(history);
124 static Call_Info *_history_call_info_search(const History *history,
125 const OFono_Call *call)
127 Call_Info *call_info;
129 long long t = ofono_call_full_start_time_get(call);
130 const char *line_id = ofono_call_line_id_get(call); /* stringshare */
132 EINA_LIST_FOREACH(history->calls->list, l, call_info) {
133 if (call_info->call == call)
135 else if (!call_info->call) {
136 if ((t > 0) && (call_info->start_time == t) &&
137 (line_id == call_info->line_id)) {
138 DBG("associated existing log %p %s (%lld) with "
142 call_info->start_time,
144 call_info->call = call;
153 static Eina_Bool _history_call_info_update(Call_Info *call_info)
155 OFono_Call_State state;
157 EINA_SAFETY_ON_NULL_RETURN_VAL(call_info->call, EINA_FALSE);
158 state = ofono_call_state_get(call_info->call);
160 if (state == OFONO_CALL_STATE_INCOMING ||
161 state == OFONO_CALL_STATE_WAITING) {
162 if (!call_info->incoming) {
163 call_info->incoming = EINA_TRUE;
166 } else if (state == OFONO_CALL_STATE_DIALING ||
167 state == OFONO_CALL_STATE_ALERTING) {
168 if (!call_info->incoming) {
169 call_info->incoming = EINA_FALSE;
172 } else if (state == OFONO_CALL_STATE_ACTIVE ||
173 state == OFONO_CALL_STATE_HELD) {
174 if (!call_info->completed) {
175 call_info->start_time = ofono_call_full_start_time_get
177 if (call_info->start_time == 0)
178 call_info->start_time = call_info->creation_time;
180 call_info->completed = EINA_TRUE;
188 static void _dial_reply(void *data, OFono_Error err,
189 OFono_Call *call __UNUSED__)
191 const char *number = data;
193 if (err != OFONO_ERROR_NONE) {
195 snprintf(buf, sizeof(buf), "Could not call: %s", number);
196 gui_simple_popup("Error", buf);
200 static void _on_item_clicked(void *data, Evas_Object *obj __UNUSED__,
203 Elm_Object_Item *it = event_info;
204 const char *number = data;
206 INF("call %s", number);
207 ofono_dial(number, NULL, _dial_reply, number);
208 elm_genlist_item_selected_set(it, EINA_FALSE);
211 static void _history_call_changed(void *data, OFono_Call *call)
213 History *history = data;
214 const char *line_id = ofono_call_line_id_get(call);
215 Call_Info *call_info;
216 OFono_Call_State state = ofono_call_state_get(call);
218 call_info = _history_call_info_search(history, call);
219 DBG("call=%p, id=%s, state=%d, completed=%d, incoming=%d, info=%p",
220 call, line_id, state,
221 call_info ? call_info->completed : EINA_FALSE,
222 call_info ? call_info->incoming : EINA_FALSE,
228 call_info = calloc(1, sizeof(Call_Info));
229 EINA_SAFETY_ON_NULL_RETURN(call_info);
231 call_info->call = call;
232 call_info->start_time = ofono_call_full_start_time_get(call);
233 call_info->creation_time = time(NULL);
234 call_info->line_id = eina_stringshare_add(line_id);
235 call_info->name = eina_stringshare_add(ofono_call_name_get(call));
236 history->calls->list =
237 eina_list_prepend(history->calls->list, call_info);
238 history->calls->dirty = EINA_TRUE;
241 if (_history_call_info_update(call_info))
242 history->calls->dirty = EINA_TRUE;
245 static void _history_call_log_save(History *history)
249 EINA_SAFETY_ON_NULL_RETURN(history->calls);
250 DBG("save history (%u calls, dirty: %d) to %s",
251 eina_list_count(history->calls->list), history->calls->dirty,
254 ecore_file_unlink(history->bkp);
255 ecore_file_mv(history->path, history->bkp);
256 efile = eet_open(history->path, EET_FILE_MODE_WRITE);
257 EINA_SAFETY_ON_NULL_RETURN(efile);
258 if (!(eet_data_write(efile,
259 history->edd_list, HISTORY_ENTRY,
260 history->calls, EET_COMPRESSION_DEFAULT)))
261 ERR("Could in the history log file");
266 static void _history_call_removed(void *data, OFono_Call *call)
269 History *history = data;
270 const char *line_id = ofono_call_line_id_get(call);
271 Call_Info *call_info;
275 call_info = _history_call_info_search(history, call);
276 DBG("call=%p, id=%s, info=%p", call, line_id, call_info);
277 EINA_SAFETY_ON_NULL_RETURN(call_info);
279 if (call_info->start_time == 0)
280 call_info->start_time = call_info->creation_time;
282 start = call_info->start_time;
285 call_info->end_time = time(NULL);
286 call_info->call = NULL;
288 if (call_info->completed)
289 INF("Call end: %s at %s", line_id, tm);
291 if (!call_info->incoming)
292 INF("Not answered: %s at %s", line_id, tm);
294 INF("Missed: %s at %s", line_id, tm);
295 if (call_info->it_missed)
296 elm_genlist_item_update(call_info->it_missed);
298 it = elm_genlist_item_prepend
299 (history->genlist_missed,
302 ELM_GENLIST_ITEM_NONE,
305 elm_genlist_item_show
306 (it, ELM_GENLIST_ITEM_SCROLLTO_IN);
307 call_info->it_missed = it;
312 history->calls->dirty = EINA_TRUE;
313 _history_call_log_save(history);
315 if (call_info->it_all)
316 elm_genlist_item_update(call_info->it_all);
318 it = elm_genlist_item_prepend(history->genlist_all,
321 ELM_GENLIST_ITEM_NONE,
324 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN);
325 call_info->it_all = it;
329 static void _call_info_free(Call_Info *call_info)
331 eina_stringshare_del(call_info->line_id);
332 eina_stringshare_del(call_info->name);
336 static void _on_del(void *data, Evas *e __UNUSED__,
337 Evas_Object *obj __UNUSED__, void *event __UNUSED__)
339 History *history = data;
340 Call_Info *call_info;
342 if (history->updater)
343 ecore_poller_del(history->updater);
345 if (history->calls->dirty)
346 _history_call_log_save(history);
348 ofono_call_removed_cb_del(callback_node_call_removed);
349 ofono_call_changed_cb_del(callback_node_call_changed);
350 eet_data_descriptor_free(history->edd);
351 eet_data_descriptor_free(history->edd_list);
352 EINA_LIST_FREE(history->calls->list, call_info)
353 _call_info_free(call_info);
354 free(history->calls);
355 elm_genlist_item_class_free(history->itc);
359 ecore_file_shutdown();
363 static void _on_hide(void *data, Evas *e __UNUSED__,
364 Evas_Object *obj __UNUSED__, void *event __UNUSED__)
366 History *history = data;
367 DBG("history became hidden");
368 _history_time_updater_stop(history);
371 static void _on_show(void *data, Evas *e __UNUSED__,
372 Evas_Object *obj __UNUSED__, void *event __UNUSED__)
374 History *history = data;
375 DBG("history became visible");
376 _history_time_updater_start(history);
379 static void _on_win_focus_out(void *data, Evas *e __UNUSED__,
380 void *event_info __UNUSED__)
382 History *history = data;
383 DBG("window is unfocused");
384 _history_time_updater_stop(history);
387 static void _on_win_focus_in(void *data, Evas *e __UNUSED__,
388 void *event_info __UNUSED__)
390 History *history = data;
391 DBG("window is focused");
392 _history_time_updater_start(history);
395 static void _history_call_info_descriptor_init(Eet_Data_Descriptor **edd,
396 Eet_Data_Descriptor **edd_list)
398 Eet_Data_Descriptor_Class eddc;
400 EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info);
401 *edd = eet_data_descriptor_stream_new(&eddc);
403 EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info_List);
404 *edd_list = eet_data_descriptor_stream_new(&eddc);
406 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
407 "completed", completed, EET_T_UCHAR);
408 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
409 "incoming", incoming, EET_T_UCHAR);
411 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
412 "start_time", start_time, EET_T_LONG_LONG);
413 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
414 "end_time", end_time, EET_T_LONG_LONG);
415 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
416 "line_id", line_id, EET_T_STRING);
417 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
418 "name", name, EET_T_STRING);
420 EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Call_Info_List, "list", list,
424 static void _history_call_log_read(History *history)
426 Call_Info *call_info;
429 Call_Info_List *calls = NULL;
432 efile = eet_open(history->path, EET_FILE_MODE_READ);
435 calls = eet_data_read(efile, history->edd_list, HISTORY_ENTRY);
440 efile = eet_open(history->bkp, EET_FILE_MODE_READ);
442 calls = eet_data_read(efile, history->edd_list,
449 calls = calloc(1, sizeof(Call_Info_List));
451 history->calls = calls;
452 EINA_SAFETY_ON_NULL_RETURN(history->calls);
454 EINA_LIST_FOREACH(history->calls->list, l, call_info) {
455 it = elm_genlist_item_append(history->genlist_all,
458 ELM_GENLIST_ITEM_NONE,
461 call_info->it_all = it;
463 if (call_info->completed)
466 it = elm_genlist_item_append(history->genlist_missed,
467 history->itc, call_info, NULL,
468 ELM_GENLIST_ITEM_NONE,
471 call_info->it_missed = it;
474 it = elm_genlist_first_item_get(history->genlist_all);
476 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
478 it = elm_genlist_first_item_get(history->genlist_missed);
480 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
483 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
486 Call_Info *call_info = data;
488 if (strncmp(part, "text.call", strlen("text.call")))
491 part += strlen("text.call.");
493 if (!strcmp(part, "name")) {
494 if (!call_info->name || call_info->name[0] == '\0')
495 return phone_format(call_info->line_id);
496 return strdup(call_info->name);
499 if (!strcmp(part, "time")) {
500 if ((call_info->completed) && (call_info->end_time))
501 return date_format(call_info->end_time);
502 return date_format(call_info->start_time);
505 /* TODO: Fetch phone type from contacts information*/
506 if (!strcmp(part, "type"))
507 return strdup("TODO:TELEPHONE TYPE");
509 ERR("Unexpected text part: %s", part);
514 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
517 Call_Info *call_info = data;
519 if (!strcmp(part, "missed"))
520 return !call_info->completed;
521 else if (!strcmp(part, "completed"))
522 return call_info->completed;
523 else if (!strcmp(part, "outgoing"))
524 return !call_info->incoming;
525 else if (!strcmp(part, "incoming"))
526 return call_info->incoming;
528 ERR("Unexpected state part: %s", part);
532 static void _on_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
533 const char *emission, const char *source __UNUSED__)
535 EINA_SAFETY_ON_NULL_RETURN(emission);
536 emission += strlen("clicked,");
538 if (!strcmp(emission, "all"))
539 elm_object_signal_emit(obj, "show,all", "gui");
541 elm_object_signal_emit(obj, "show,missed", "gui");
544 static void _on_more_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
545 const char *emission __UNUSED__,
546 const char *source __UNUSED__)
551 static Evas_Object *_item_content_get(void *data __UNUSED__, Evas_Object *obj,
552 const char *part __UNUSED__)
556 btn = gui_layout_add(obj, "history/img");
557 EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
558 elm_object_signal_callback_add(btn, "clicked,more", "gui",
559 _on_more_clicked, NULL);
560 evas_object_propagate_events_set(btn, EINA_FALSE);
565 Evas_Object *history_add(Evas_Object *parent)
570 const char *config_path;
571 char *path, base_dir[PATH_MAX];
572 Elm_Genlist_Item_Class *itc;
573 Evas_Object *obj, *genlist_all, *genlist_missed;
577 history = calloc(1, sizeof(History));
578 EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
580 history->self = obj = gui_layout_add(parent, "history_bg");
581 EINA_SAFETY_ON_NULL_GOTO(obj, err_layout);
583 genlist_all = elm_genlist_add(obj);
584 EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
585 elm_object_style_set(genlist_all, "history");
587 genlist_missed = elm_genlist_add(obj);
588 EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
589 elm_object_style_set(genlist_missed, "history");
591 itc = elm_genlist_item_class_new();
592 EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
593 itc->item_style = "history";
594 itc->func.text_get = _item_label_get;
595 itc->func.content_get = _item_content_get;
596 itc->func.state_get = _item_state_get;
597 itc->func.del = NULL;
598 history->genlist_all = genlist_all;
599 history->genlist_missed = genlist_missed;
602 elm_object_part_content_set(obj, "elm.swallow.all", genlist_all);
603 elm_object_part_content_set(obj, "elm.swallow.missed", genlist_missed);
604 elm_object_signal_emit(obj, "show,all", "gui");
605 elm_object_signal_callback_add(obj, "clicked,*", "gui",
608 config_path = efreet_config_home_get();
609 snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
611 ecore_file_mkpath(base_dir);
612 r = asprintf(&path, "%s/%s/history.eet", config_path, PACKAGE_NAME);
617 history->path = path;
618 r = asprintf(&path, "%s/%s/history.eet.bkp", config_path,
626 _history_call_info_descriptor_init(&history->edd, &history->edd_list);
627 _history_call_log_read(history);
628 EINA_SAFETY_ON_NULL_GOTO(history->calls, err_log_read);
629 evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
631 evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide,
633 evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show,
636 e = evas_object_evas_get(obj);
637 evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_OUT,
638 _on_win_focus_out, history);
639 evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_IN,
640 _on_win_focus_in, history);
642 callback_node_call_changed =
643 ofono_call_changed_cb_add(_history_call_changed, history);
644 callback_node_call_removed =
645 ofono_call_removed_cb_add(_history_call_removed, history);
651 eet_data_descriptor_free(history->edd);
652 eet_data_descriptor_free(history->edd_list);
656 elm_genlist_item_class_free(itc);
661 ecore_file_shutdown();