move dial() from util.h to gui_dial() in gui.h
[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 #ifndef EET_COMPRESSION_DEFAULT
17 #define EET_COMPRESSION_DEFAULT 1
18 #endif
19
20 #define HISTORY_ENTRY "history"
21
22 typedef struct _Call_Info_List {
23         Eina_List *list;
24         Eina_Bool dirty;
25 } Call_Info_List;
26
27 typedef struct _History {
28         char *path, *bkp;
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 *self;
34         Evas_Object *clear_popup;
35         Evas_Object *genlist_all, *genlist_missed;
36         Ecore_Poller *updater;
37         double last_update;
38 } History;
39
40 typedef struct _Call_Info {
41         long long start_time;
42         long long end_time;
43         long long creation_time; /* not in edd */
44         const char *line_id;
45         const char *name;
46         Eina_Bool completed;
47         Eina_Bool incoming;
48         const OFono_Call *call; /* not in edd */
49         History *history;
50         Elm_Object_Item *it_all; /*not in edd */
51         Elm_Object_Item *it_missed; /*not in edd */
52         const Contact_Info *contact; /* not in edd */
53         const char *contact_type; /* not in edd */
54         double contact_last; /* not in edd, last time it was searched */
55 #define CONTACT_LAST_THRESHOLD 1.0
56 } Call_Info;
57
58 static OFono_Callback_List_Call_Node *callback_node_call_removed = NULL;
59 static OFono_Callback_List_Call_Node *callback_node_call_changed = NULL;
60
61 static Eina_Bool _history_time_updater(void *data)
62 {
63         History *ctx = data;
64         Elm_Object_Item *it;
65         double now = ecore_loop_time_get();
66         const double interval_threshold = 30.0;
67         const long long update_threshold = time(NULL) - WEEK - DAY;
68
69         /*
70          * NOTE ABOUT CONSTANTS:
71          *
72          * - interval_threshold: to avoid updating too often (window
73          *   lost and gained focus, object hidden or shown), we limit
74          *   updates to this minimum interval. The poller should run
75          *   every 60 seconds.
76          *
77          * - update_threshold: since we format strings over a week as
78          *   fixed string (often day-month-year, not relative to
79          *   today), we can stop flagging list items as updated. We
80          *   give it a day of slack so we can be sure to update every
81          *   item (for held and conferences, you may have items that
82          *   are close in time but slightly out of order as items are
83          *   prepended as the calls are removed from ofono, then
84          *   history is not strictly in 'time' order). We must
85          *   stop iterating after update_threshold so users that never
86          *   deleted history and have thousand items will not
87          *   uselessly update all the thousand items.
88          */
89
90         if (!ctx->calls->list) {
91                 ctx->updater = NULL;
92                 return EINA_FALSE;
93         }
94
95         if (now - ctx->last_update < interval_threshold)
96                 return EINA_TRUE;
97         ctx->last_update = now;
98
99         it = elm_genlist_first_item_get(ctx->genlist_all);
100         for (; it != NULL; it = elm_genlist_item_next_get(it)) {
101                 const Call_Info *call_info = elm_object_item_data_get(it);
102                 long long t = call_info->end_time;
103                 if (EINA_UNLIKELY(t == 0)) {
104                         t = call_info->start_time;
105                         if (EINA_UNLIKELY(t == 0))
106                                 t = call_info->creation_time;
107                 }
108                 if (EINA_UNLIKELY(t < update_threshold))
109                         break;
110                 elm_genlist_item_update(it);
111         }
112
113         it = elm_genlist_first_item_get(ctx->genlist_missed);
114         for (; it != NULL; it = elm_genlist_item_next_get(it)) {
115                 const Call_Info *call_info = elm_object_item_data_get(it);
116                 long long t = call_info->end_time;
117                 if (EINA_UNLIKELY(t == 0)) {
118                         t = call_info->start_time;
119                         if (EINA_UNLIKELY(t == 0))
120                                 t = call_info->creation_time;
121                 }
122                 if (EINA_UNLIKELY(t < update_threshold))
123                         break;
124                 elm_genlist_item_update(it);
125         }
126
127         return EINA_TRUE;
128 }
129
130 static void _history_time_updater_stop(History *history)
131 {
132         Evas *e = evas_object_evas_get(history->self);
133         Eina_Bool win_focused = evas_focus_state_get(e);
134         Eina_Bool obj_visible = evas_object_visible_get(history->self);
135
136         DBG("poller %p, win_focused=%hhu, obj_visible=%hhu",
137                 history->updater, win_focused, obj_visible);
138         if (!history->updater)
139                 return;
140         if (win_focused && obj_visible)
141                 return;
142
143         DBG("delete poller %p", history->updater);
144         ecore_poller_del(history->updater);
145         history->updater = NULL;
146 }
147
148 static void _history_time_updater_start(History *history)
149 {
150         Evas *e = evas_object_evas_get(history->self);
151         Eina_Bool win_focused = evas_focus_state_get(e);
152         Eina_Bool obj_visible = evas_object_visible_get(history->self);
153
154         DBG("poller %p, win_focused=%hhu, obj_visible=%hhu",
155                 history->updater, win_focused, obj_visible);
156         if (history->updater)
157                 return;
158         if (!history->calls->list)
159                 return;
160         if ((!win_focused) || (!obj_visible))
161                 return;
162
163         DBG("start poller");
164         /* ECORE_POLLER_CORE is 1/8th of second. */
165         history->updater = ecore_poller_add(ECORE_POLLER_CORE, 8 * 60,
166                                                 _history_time_updater,
167                                                 history);
168         _history_time_updater(history);
169 }
170
171 static Call_Info *_history_call_info_search(const History *history,
172                                                 const OFono_Call *call)
173 {
174         Call_Info *call_info;
175         Eina_List *l;
176         long long t = ofono_call_full_start_time_get(call);
177         const char *line_id = ofono_call_line_id_get(call); /* stringshare */
178
179         EINA_LIST_FOREACH(history->calls->list, l, call_info) {
180                 if (call_info->call == call)
181                         return call_info;
182                 else if (!call_info->call) {
183                         if ((t > 0) && (call_info->start_time == t) &&
184                                 (line_id == call_info->line_id)) {
185                                 DBG("associated existing log %p %s (%lld) with "
186                                         "call %p %s (%lld)",
187                                         call_info,
188                                         call_info->line_id,
189                                         call_info->start_time,
190                                         call, line_id, t);
191                                 call_info->call = call;
192                                 return call_info;
193                         }
194                 }
195         }
196
197         return NULL;
198 }
199
200 static Eina_Bool _history_call_info_update(Call_Info *call_info)
201 {
202         OFono_Call_State state;
203
204         EINA_SAFETY_ON_NULL_RETURN_VAL(call_info->call, EINA_FALSE);
205         state = ofono_call_state_get(call_info->call);
206
207         if (state == OFONO_CALL_STATE_INCOMING ||
208                 state == OFONO_CALL_STATE_WAITING) {
209                 if (!call_info->incoming) {
210                         call_info->incoming = EINA_TRUE;
211                         return EINA_TRUE;
212                 }
213         } else if (state == OFONO_CALL_STATE_DIALING ||
214                         state == OFONO_CALL_STATE_ALERTING) {
215                 if (!call_info->incoming) {
216                         call_info->incoming = EINA_FALSE;
217                         return EINA_TRUE;
218                 }
219         } else if (state == OFONO_CALL_STATE_ACTIVE ||
220                         state == OFONO_CALL_STATE_HELD) {
221                 if (!call_info->completed) {
222                         call_info->start_time = ofono_call_full_start_time_get
223                                 (call_info->call);
224                         if (call_info->start_time == 0)
225                                 call_info->start_time = call_info->creation_time;
226
227                         call_info->completed = EINA_TRUE;
228                         return EINA_TRUE;
229                 }
230         }
231
232         return EINA_FALSE;
233 }
234
235 static void _on_item_clicked(void *data, Evas_Object *obj __UNUSED__,
236                                 void *event_info)
237 {
238         Elm_Object_Item *it = event_info;
239         const char *number = data;
240
241         INF("call %s", number);
242         gui_dial(number);
243         elm_genlist_item_selected_set(it, EINA_FALSE);
244 }
245
246 static void _history_call_changed(void *data, OFono_Call *call)
247 {
248         History *history = data;
249         const char *line_id = ofono_call_line_id_get(call);
250         Call_Info *call_info;
251         OFono_Call_State state = ofono_call_state_get(call);
252
253         call_info = _history_call_info_search(history, call);
254         DBG("call=%p, id=%s, state=%d, completed=%d, incoming=%d, info=%p",
255                 call, line_id, state,
256                 call_info ? call_info->completed : EINA_FALSE,
257                 call_info ? call_info->incoming : EINA_FALSE,
258                 call_info);
259
260         if (call_info)
261                 goto end;
262
263         call_info = calloc(1, sizeof(Call_Info));
264         EINA_SAFETY_ON_NULL_RETURN(call_info);
265
266         call_info->call = call;
267         call_info->start_time = ofono_call_full_start_time_get(call);
268         call_info->creation_time = time(NULL);
269         if (call_info->start_time == 0)
270                 call_info->start_time = call_info->creation_time;
271         call_info->line_id = eina_stringshare_add(line_id);
272         call_info->name = eina_stringshare_add(ofono_call_name_get(call));
273         history->calls->list =
274                 eina_list_prepend(history->calls->list, call_info);
275         history->calls->dirty = EINA_TRUE;
276
277 end:
278         if (_history_call_info_update(call_info))
279                 history->calls->dirty = EINA_TRUE;
280 }
281
282 static void _history_call_log_save(History *history)
283 {
284         Eet_File *efile;
285
286         EINA_SAFETY_ON_NULL_RETURN(history->calls);
287         DBG("save history (%u calls, dirty: %d) to %s",
288                 eina_list_count(history->calls->list), history->calls->dirty,
289                 history->path);
290
291         ecore_file_unlink(history->bkp);
292         ecore_file_mv(history->path, history->bkp);
293         efile = eet_open(history->path, EET_FILE_MODE_WRITE);
294         EINA_SAFETY_ON_NULL_RETURN(efile);
295         if (!(eet_data_write(efile,
296                                 history->edd_list, HISTORY_ENTRY,
297                                 history->calls, EET_COMPRESSION_DEFAULT)))
298                 ERR("Could in the history log file");
299
300         eet_close(efile);
301 }
302
303 static void _history_call_removed(void *data, OFono_Call *call)
304 {
305         Elm_Object_Item *it;
306         History *history = data;
307         const char *line_id = ofono_call_line_id_get(call);
308         Call_Info *call_info;
309         time_t start;
310         char *tm;
311
312         call_info = _history_call_info_search(history, call);
313         DBG("call=%p, id=%s, info=%p", call, line_id, call_info);
314         EINA_SAFETY_ON_NULL_RETURN(call_info);
315
316         if (call_info->start_time == 0)
317                 call_info->start_time = call_info->creation_time;
318
319         start = call_info->start_time;
320         tm = ctime(&start);
321
322         call_info->end_time = time(NULL);
323         call_info->call = NULL;
324
325         if (call_info->completed)
326                 INF("Call end:  %s at %s", line_id, tm);
327         else {
328                 if (!call_info->incoming)
329                         INF("Not answered: %s at %s", line_id, tm);
330                 else {
331                         INF("Missed: %s at %s", line_id, tm);
332                         if (call_info->it_missed)
333                                 elm_genlist_item_update(call_info->it_missed);
334                         else {
335                                 it = elm_genlist_item_prepend
336                                         (history->genlist_missed,
337                                                 history->itc,
338                                                 call_info, NULL,
339                                                 ELM_GENLIST_ITEM_NONE,
340                                                 _on_item_clicked,
341                                                 call_info->line_id);
342                                 elm_genlist_item_show
343                                         (it, ELM_GENLIST_ITEM_SCROLLTO_IN);
344                                 call_info->it_missed = it;
345                                 call_info->history = history;
346                                 _history_time_updater_start(history);
347                         }
348                 }
349         }
350
351         history->calls->dirty = EINA_TRUE;
352         _history_call_log_save(history);
353
354         if (call_info->it_all)
355                 elm_genlist_item_update(call_info->it_all);
356         else {
357                 it = elm_genlist_item_prepend(history->genlist_all,
358                                                 history->itc,
359                                                 call_info, NULL,
360                                                 ELM_GENLIST_ITEM_NONE,
361                                                 _on_item_clicked,
362                                                 call_info->line_id);
363                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN);
364                 call_info->it_all = it;
365                 call_info->history = history;
366                 _history_time_updater_start(history);
367         }
368 }
369
370 static void _on_contact_del(void *data, const Contact_Info *contact __UNUSED__)
371 {
372         Call_Info *call_info = data;
373         call_info->contact = NULL;
374         call_info->contact_type = NULL;
375         call_info->contact_last = 0.0;
376 }
377
378 static void _on_contact_changed(void *data, Contact_Info *contact)
379 {
380         Call_Info *call_info = data;
381
382         if (contact_info_number_check(contact, call_info->line_id))
383                 goto update;
384
385         contact_info_on_del_callback_del(contact, _on_contact_del, call_info);
386         contact_info_on_changed_callback_del(contact, _on_contact_changed,
387                                                 call_info);
388
389         call_info->contact = NULL;
390         call_info->contact_type = NULL;
391         call_info->contact_last = 0.0;
392
393 update:
394         if (call_info->it_all)
395                 elm_genlist_item_update(call_info->it_all);
396         if (call_info->it_missed)
397                 elm_genlist_item_update(call_info->it_missed);
398 }
399
400 static void _call_info_free(Call_Info *call_info)
401 {
402         if (call_info->contact) {
403                 Contact_Info *contact = (Contact_Info *)call_info->contact;
404                 contact_info_on_del_callback_del(contact, _on_contact_del,
405                                                         call_info);
406                 contact_info_on_changed_callback_del(contact,
407                                                         _on_contact_changed,
408                                                         call_info);
409         }
410
411         eina_stringshare_del(call_info->line_id);
412         eina_stringshare_del(call_info->name);
413         free(call_info);
414 }
415
416 static void _on_del(void *data, Evas *e __UNUSED__,
417                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
418 {
419         History *history = data;
420         Call_Info *call_info;
421
422         if (history->updater)
423                 ecore_poller_del(history->updater);
424
425         if (history->calls->dirty)
426                 _history_call_log_save(history);
427
428         ofono_call_removed_cb_del(callback_node_call_removed);
429         ofono_call_changed_cb_del(callback_node_call_changed);
430         eet_data_descriptor_free(history->edd);
431         eet_data_descriptor_free(history->edd_list);
432         EINA_LIST_FREE(history->calls->list, call_info)
433                 _call_info_free(call_info);
434         free(history->calls);
435         elm_genlist_item_class_free(history->itc);
436         free(history->path);
437         free(history->bkp);
438         free(history);
439         ecore_file_shutdown();
440         eet_shutdown();
441 }
442
443 static void _on_hide(void *data, Evas *e __UNUSED__,
444                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
445 {
446         History *history = data;
447         DBG("history became hidden");
448         _history_time_updater_stop(history);
449 }
450
451 static void _on_show(void *data, Evas *e __UNUSED__,
452                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
453 {
454         History *history = data;
455         DBG("history became visible");
456         _history_time_updater_start(history);
457 }
458
459 static void _on_win_focus_out(void *data, Evas *e __UNUSED__,
460                                 void *event_info __UNUSED__)
461 {
462         History *history = data;
463         DBG("window is unfocused");
464         _history_time_updater_stop(history);
465 }
466
467 static void _on_win_focus_in(void *data, Evas *e __UNUSED__,
468                                 void *event_info __UNUSED__)
469 {
470         History *history = data;
471         DBG("window is focused");
472         _history_time_updater_start(history);
473 }
474
475 static void _history_call_info_descriptor_init(Eet_Data_Descriptor **edd,
476                                                 Eet_Data_Descriptor **edd_list)
477 {
478         Eet_Data_Descriptor_Class eddc;
479
480         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info);
481         *edd = eet_data_descriptor_stream_new(&eddc);
482
483         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info_List);
484         *edd_list = eet_data_descriptor_stream_new(&eddc);
485
486         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
487                                         "completed", completed, EET_T_UCHAR);
488         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
489                                         "incoming", incoming, EET_T_UCHAR);
490
491         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
492                                         "start_time", start_time, EET_T_LONG_LONG);
493         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
494                                         "end_time", end_time, EET_T_LONG_LONG);
495         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
496                                         "line_id", line_id, EET_T_STRING);
497         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
498                                         "name", name, EET_T_STRING);
499
500         EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Call_Info_List, "list", list,
501                                         *edd);
502 }
503
504 static void _history_call_log_read(History *history)
505 {
506         Call_Info *call_info;
507         Eina_List *l;
508         Eet_File *efile;
509         Call_Info_List *calls = NULL;
510         Elm_Object_Item *it;
511
512         efile = eet_open(history->path, EET_FILE_MODE_READ);
513
514         if (efile) {
515                 calls = eet_data_read(efile, history->edd_list, HISTORY_ENTRY);
516                 eet_close(efile);
517         }
518
519         if (!calls) {
520                 efile = eet_open(history->bkp, EET_FILE_MODE_READ);
521                 if (efile) {
522                         calls = eet_data_read(efile, history->edd_list,
523                                                 HISTORY_ENTRY);
524                         eet_close(efile);
525                 }
526         }
527
528         if (!calls)
529                 calls = calloc(1, sizeof(Call_Info_List));
530
531         history->calls = calls;
532         EINA_SAFETY_ON_NULL_RETURN(history->calls);
533
534         EINA_LIST_FOREACH(history->calls->list, l, call_info) {
535                 it = elm_genlist_item_append(history->genlist_all,
536                                                 history->itc,
537                                                 call_info, NULL,
538                                                 ELM_GENLIST_ITEM_NONE,
539                                                 _on_item_clicked,
540                                                 call_info->line_id);
541                 call_info->it_all = it;
542                 call_info->history = history;
543
544                 if (call_info->completed)
545                         continue;
546
547                 it = elm_genlist_item_append(history->genlist_missed,
548                                                 history->itc, call_info, NULL,
549                                                 ELM_GENLIST_ITEM_NONE,
550                                                 _on_item_clicked,
551                                                 call_info->line_id);
552                 call_info->it_missed = it;
553                 call_info->history = history;
554         }
555
556         it = elm_genlist_first_item_get(history->genlist_all);
557         if (it)
558                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
559
560         it = elm_genlist_first_item_get(history->genlist_missed);
561         if (it)
562                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
563 }
564
565 static void _history_call_info_del(Call_Info *call_info)
566 {
567         History *ctx = call_info->history;
568
569         EINA_SAFETY_ON_NULL_RETURN(ctx);
570
571         call_info->call = NULL;
572         if (call_info->it_all)
573                 elm_object_item_del(call_info->it_all);
574         if (call_info->it_missed)
575                 elm_object_item_del(call_info->it_missed);
576
577         ctx->calls->list = eina_list_remove(ctx->calls->list, call_info);
578         ctx->calls->dirty = EINA_TRUE;
579         _history_call_log_save(ctx);
580
581         if ((!ctx->calls->list) && (ctx->updater)) {
582                 ecore_poller_del(ctx->updater);
583                 ctx->updater = NULL;
584         }
585
586         _call_info_free(call_info);
587 }
588
589 static void _history_clear_do(void *data, Evas_Object *obj __UNUSED__,
590                                 void *event_info __UNUSED__)
591 {
592         History *ctx = data;
593         Call_Info *call_info;
594
595         DBG("ctx=%p, deleting %u entries",
596                 ctx, eina_list_count(ctx->calls->list));
597
598         evas_object_del(ctx->clear_popup);
599         ctx->clear_popup = NULL;
600
601         elm_genlist_clear(ctx->genlist_all);
602         elm_genlist_clear(ctx->genlist_missed);
603
604         EINA_LIST_FREE(ctx->calls->list, call_info)
605                 _call_info_free(call_info);
606
607         ctx->calls->dirty = EINA_TRUE;
608         _history_call_log_save(ctx);
609
610         if (ctx->updater) {
611                 ecore_poller_del(ctx->updater);
612                 ctx->updater = NULL;
613         }
614
615         elm_object_signal_emit(ctx->self, "toggle,off,edit", "gui");
616 }
617
618 static void _history_clear_cancel(void *data, Evas_Object *obj __UNUSED__,
619                                         void *event_info __UNUSED__)
620 {
621         History *ctx = data;
622
623         DBG("ctx=%p", ctx);
624
625         evas_object_del(ctx->clear_popup);
626         ctx->clear_popup = NULL;
627 }
628
629 static void _history_clear(History *ctx)
630 {
631         Evas_Object *p;
632
633         EINA_SAFETY_ON_TRUE_RETURN(ctx->clear_popup != NULL);
634
635         ctx->clear_popup = p = gui_simple_popup("Clear History",
636                                 "Do you want to clear all history entries?");
637
638         gui_simple_popup_buttons_set(p,
639                                         "Dismiss",
640                                         "dialer",
641                                         _history_clear_cancel,
642                                         "Yes, Clear",
643                                         "dialer-caution",
644                                         _history_clear_do,
645                                         ctx);
646 }
647
648 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
649                                 const char *part)
650 {
651         Call_Info *call_info = data;
652
653         if (strncmp(part, "text.call", strlen("text.call")))
654                 return NULL;
655
656         part += strlen("text.call.");
657
658         if (!call_info->contact) {
659                 double now = ecore_loop_time_get();
660                 double diff = now - call_info->contact_last;
661                 if (diff > CONTACT_LAST_THRESHOLD) {
662                         Contact_Info *contact = gui_contact_search(
663                                 call_info->line_id, &(call_info->contact_type));
664
665                         call_info->contact_last = now;
666                         call_info->contact = contact;
667                         if (contact) {
668                                 contact_info_on_del_callback_add(
669                                         contact, _on_contact_del, call_info);
670                                 contact_info_on_changed_callback_add(
671                                         contact, _on_contact_changed,
672                                         call_info);
673                         }
674                 }
675         }
676
677         if (!strcmp(part, "name")) {
678                 if (!call_info->contact)
679                         return strdup(call_info->line_id);
680                 return strdup(contact_info_full_name_get(call_info->contact));
681         }
682
683         if (!strcmp(part, "time")) {
684                 if ((call_info->completed) && (call_info->end_time))
685                         return date_format(call_info->end_time);
686                 return date_format(call_info->start_time);
687         }
688
689         if (!strcmp(part, "type")) {
690                 if (!call_info->contact_type)
691                         return strdup("Unknown");
692                 return strdup(call_info->contact_type);
693         }
694
695         ERR("Unexpected text part: %s", part);
696         return NULL;
697 }
698
699
700 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
701                                         const char  *part)
702 {
703         Call_Info *call_info = data;
704
705         if (!strcmp(part, "missed"))
706                 return !call_info->completed;
707         else if (!strcmp(part, "completed"))
708                 return call_info->completed;
709         else if (!strcmp(part, "outgoing"))
710                 return !call_info->incoming;
711         else if (!strcmp(part, "incoming"))
712                 return call_info->incoming;
713
714         ERR("Unexpected state part: %s", part);
715         return EINA_FALSE;
716 }
717
718 static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
719                         const char *emission, const char *source __UNUSED__)
720 {
721         History *ctx = data;
722
723         EINA_SAFETY_ON_NULL_RETURN(emission);
724         emission += strlen("clicked,");
725
726         DBG("ctx=%p, signal: %s", ctx, emission);
727
728         if (!strcmp(emission, "all"))
729                 elm_object_signal_emit(obj, "show,all", "gui");
730         else if (!strcmp(emission, "missed"))
731                 elm_object_signal_emit(obj, "show,missed", "gui");
732         else if (!strcmp(emission, "clear"))
733                 _history_clear(ctx);
734         else if (!strcmp(emission, "edit")) {
735                 elm_object_signal_emit(obj, "toggle,on,edit", "gui");
736                 elm_genlist_decorate_mode_set(ctx->genlist_all, EINA_TRUE);
737                 elm_genlist_decorate_mode_set(ctx->genlist_missed, EINA_TRUE);
738         } else if (!strcmp(emission, "edit,done")) {
739                 elm_object_signal_emit(obj, "toggle,off,edit", "gui");
740                 elm_genlist_decorate_mode_set(ctx->genlist_all, EINA_FALSE);
741                 elm_genlist_decorate_mode_set(ctx->genlist_missed, EINA_FALSE);
742         }
743 }
744
745 static void _on_more_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
746                                 const char *emission __UNUSED__,
747                                 const char *source __UNUSED__)
748 {
749         DBG("TODO");
750 }
751
752 static void _on_del_clicked(void *data, Evas_Object *obj __UNUSED__,
753                                 void *event_info __UNUSED__)
754 {
755         Call_Info *call_info = data;
756         DBG("call_info=%p, items all=%p missed=%p",
757                 call_info, call_info->it_all, call_info->it_missed);
758         _history_call_info_del(call_info);
759 }
760
761 static Evas_Object *_item_content_get(void *data, Evas_Object *obj,
762                                         const char *part)
763 {
764         Evas_Object *btn = NULL;
765
766         if (strcmp(part, "call.swallow.more") == 0) {
767                 btn = gui_layout_add(obj, "history/img");
768                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
769                 elm_object_signal_callback_add(btn, "clicked,more", "gui",
770                                                 _on_more_clicked, NULL);
771                 evas_object_propagate_events_set(btn, EINA_FALSE);
772         } else if (strcmp(part, "call.swallow.delete") == 0) {
773                 btn = elm_button_add(obj);
774                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
775                 elm_object_style_set(btn, "history-delete");
776                 elm_object_text_set(btn, "delete");
777                 evas_object_smart_callback_add(btn, "clicked", _on_del_clicked,
778                                                 data);
779                 evas_object_propagate_events_set(btn, EINA_FALSE);
780         } else
781                 ERR("unknown content part '%s'", part);
782
783         return btn;
784 }
785
786 static void _on_list_slide_enter(void *data __UNUSED__,
787                                         Evas_Object *obj,
788                                         void *event_info)
789 {
790         Elm_Object_Item *it = elm_genlist_decorated_item_get(obj);
791         DBG("cancel decorated item=%p", it);
792         if (it)
793                 elm_genlist_item_decorate_mode_set(it, "slide", EINA_FALSE);
794
795         it = event_info;
796         EINA_SAFETY_ON_NULL_RETURN(it);
797         DBG("it=%p", it);
798         elm_genlist_item_decorate_mode_set(it, "slide", EINA_TRUE);
799 }
800
801 static void _on_list_slide_cancel(void *data __UNUSED__,
802                                         Evas_Object *obj,
803                                         void *event_info __UNUSED__)
804 {
805         Elm_Object_Item *it = elm_genlist_decorated_item_get(obj);
806         DBG("it=%p", it);
807         if (it)
808                 elm_genlist_item_decorate_mode_set(it, "slide", EINA_FALSE);
809 }
810
811 Evas_Object *history_add(Evas_Object *parent)
812 {
813         int r;
814         History *history;
815         Evas *e;
816         const char *config_path;
817         char *path, base_dir[PATH_MAX];
818         Elm_Genlist_Item_Class *itc;
819         Evas_Object *obj, *genlist_all, *genlist_missed;
820
821         eet_init();
822         ecore_file_init();
823         history = calloc(1, sizeof(History));
824         EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
825
826         history->self = obj = gui_layout_add(parent, "history_bg");
827         EINA_SAFETY_ON_NULL_GOTO(obj, err_layout);
828
829         genlist_all = elm_genlist_add(obj);
830         EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
831         elm_object_style_set(genlist_all, "history");
832         elm_genlist_homogeneous_set(genlist_all, EINA_TRUE);
833
834         evas_object_smart_callback_add(genlist_all, "drag,start,right",
835                                         _on_list_slide_enter, history);
836         evas_object_smart_callback_add(genlist_all, "drag,start,left",
837                                         _on_list_slide_cancel, history);
838         evas_object_smart_callback_add(genlist_all, "drag,start,down",
839                                         _on_list_slide_cancel, history);
840         evas_object_smart_callback_add(genlist_all, "drag,start,up",
841                                         _on_list_slide_cancel, history);
842
843         genlist_missed = elm_genlist_add(obj);
844         EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
845         elm_object_style_set(genlist_missed, "history");
846         elm_genlist_homogeneous_set(genlist_missed, EINA_TRUE);
847
848         evas_object_smart_callback_add(genlist_missed, "drag,start,right",
849                                         _on_list_slide_enter, history);
850         evas_object_smart_callback_add(genlist_missed, "drag,start,left",
851                                         _on_list_slide_cancel, history);
852         evas_object_smart_callback_add(genlist_missed, "drag,start,down",
853                                         _on_list_slide_cancel, history);
854         evas_object_smart_callback_add(genlist_missed, "drag,start,up",
855                                         _on_list_slide_cancel, history);
856
857         itc = elm_genlist_item_class_new();
858         EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
859         itc->item_style = "history";
860         itc->func.text_get = _item_label_get;
861         itc->func.content_get = _item_content_get;
862         itc->func.state_get = _item_state_get;
863         itc->func.del = NULL;
864         itc->decorate_all_item_style = "history-delete";
865         itc->decorate_item_style = "history-delete";
866         history->genlist_all = genlist_all;
867         history->genlist_missed = genlist_missed;
868         history->itc = itc;
869
870         elm_object_part_content_set(obj, "elm.swallow.all", genlist_all);
871         elm_object_part_content_set(obj, "elm.swallow.missed", genlist_missed);
872         elm_object_signal_emit(obj, "show,all", "gui");
873         elm_object_signal_callback_add(obj, "clicked,*", "gui",
874                                         _on_clicked, history);
875
876         config_path = efreet_config_home_get();
877         snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
878                         PACKAGE_NAME);
879         ecore_file_mkpath(base_dir);
880         r = asprintf(&path,  "%s/%s/history.eet", config_path, PACKAGE_NAME);
881
882         if (r < 0)
883                 goto err_item_class;
884
885         history->path = path;
886         r = asprintf(&path,  "%s/%s/history.eet.bkp", config_path,
887                         PACKAGE_NAME);
888
889         if (r < 0)
890                 goto err_path;
891
892         history->bkp = path;
893
894         _history_call_info_descriptor_init(&history->edd, &history->edd_list);
895         _history_call_log_read(history);
896         EINA_SAFETY_ON_NULL_GOTO(history->calls, err_log_read);
897         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
898                                         history);
899         evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide,
900                                         history);
901         evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show,
902                                         history);
903
904         e = evas_object_evas_get(obj);
905         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_OUT,
906                                 _on_win_focus_out, history);
907         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_IN,
908                                 _on_win_focus_in, history);
909
910         callback_node_call_changed =
911                 ofono_call_changed_cb_add(_history_call_changed, history);
912         callback_node_call_removed =
913                 ofono_call_removed_cb_add(_history_call_removed, history);
914
915         return obj;
916
917 err_log_read:
918         free(history->bkp);
919         eet_data_descriptor_free(history->edd);
920         eet_data_descriptor_free(history->edd_list);
921 err_path:
922         free(history->path);
923 err_item_class:
924         elm_genlist_item_class_free(itc);
925 err_object_new:
926         free(obj);
927 err_layout:
928         free(history);
929         ecore_file_shutdown();
930         eet_shutdown();
931         return NULL;
932 }