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