more friendly error messages.
[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 _dial_reply(void *data, OFono_Error err,
236                         OFono_Call *call __UNUSED__)
237 {
238         const char *number = data;
239
240         if (err != OFONO_ERROR_NONE) {
241                 char buf[1024];
242                 const char *msg = ofono_error_message_get(err);
243                 snprintf(buf, sizeof(buf), "Could not call %s: %s",
244                                 number, msg);
245                 gui_simple_popup("Error", buf);
246         }
247 }
248
249 static void _on_item_clicked(void *data, Evas_Object *obj __UNUSED__,
250                                 void *event_info)
251 {
252         Elm_Object_Item *it = event_info;
253         const char *number = data;
254
255         INF("call %s", number);
256         ofono_dial(number, NULL, _dial_reply, number);
257         elm_genlist_item_selected_set(it, EINA_FALSE);
258 }
259
260 static void _history_call_changed(void *data, OFono_Call *call)
261 {
262         History *history = data;
263         const char *line_id = ofono_call_line_id_get(call);
264         Call_Info *call_info;
265         OFono_Call_State state = ofono_call_state_get(call);
266
267         call_info = _history_call_info_search(history, call);
268         DBG("call=%p, id=%s, state=%d, completed=%d, incoming=%d, info=%p",
269                 call, line_id, state,
270                 call_info ? call_info->completed : EINA_FALSE,
271                 call_info ? call_info->incoming : EINA_FALSE,
272                 call_info);
273
274         if (call_info)
275                 goto end;
276
277         call_info = calloc(1, sizeof(Call_Info));
278         EINA_SAFETY_ON_NULL_RETURN(call_info);
279
280         call_info->call = call;
281         call_info->start_time = ofono_call_full_start_time_get(call);
282         call_info->creation_time = time(NULL);
283         if (call_info->start_time == 0)
284                 call_info->start_time = call_info->creation_time;
285         call_info->line_id = eina_stringshare_add(line_id);
286         call_info->name = eina_stringshare_add(ofono_call_name_get(call));
287         history->calls->list =
288                 eina_list_prepend(history->calls->list, call_info);
289         history->calls->dirty = EINA_TRUE;
290
291 end:
292         if (_history_call_info_update(call_info))
293                 history->calls->dirty = EINA_TRUE;
294 }
295
296 static void _history_call_log_save(History *history)
297 {
298         Eet_File *efile;
299
300         EINA_SAFETY_ON_NULL_RETURN(history->calls);
301         DBG("save history (%u calls, dirty: %d) to %s",
302                 eina_list_count(history->calls->list), history->calls->dirty,
303                 history->path);
304
305         ecore_file_unlink(history->bkp);
306         ecore_file_mv(history->path, history->bkp);
307         efile = eet_open(history->path, EET_FILE_MODE_WRITE);
308         EINA_SAFETY_ON_NULL_RETURN(efile);
309         if (!(eet_data_write(efile,
310                                 history->edd_list, HISTORY_ENTRY,
311                                 history->calls, EET_COMPRESSION_DEFAULT)))
312                 ERR("Could in the history log file");
313
314         eet_close(efile);
315 }
316
317 static void _history_call_removed(void *data, OFono_Call *call)
318 {
319         Elm_Object_Item *it;
320         History *history = data;
321         const char *line_id = ofono_call_line_id_get(call);
322         Call_Info *call_info;
323         time_t start;
324         char *tm;
325
326         call_info = _history_call_info_search(history, call);
327         DBG("call=%p, id=%s, info=%p", call, line_id, call_info);
328         EINA_SAFETY_ON_NULL_RETURN(call_info);
329
330         if (call_info->start_time == 0)
331                 call_info->start_time = call_info->creation_time;
332
333         start = call_info->start_time;
334         tm = ctime(&start);
335
336         call_info->end_time = time(NULL);
337         call_info->call = NULL;
338
339         if (call_info->completed)
340                 INF("Call end:  %s at %s", line_id, tm);
341         else {
342                 if (!call_info->incoming)
343                         INF("Not answered: %s at %s", line_id, tm);
344                 else {
345                         INF("Missed: %s at %s", line_id, tm);
346                         if (call_info->it_missed)
347                                 elm_genlist_item_update(call_info->it_missed);
348                         else {
349                                 it = elm_genlist_item_prepend
350                                         (history->genlist_missed,
351                                                 history->itc,
352                                                 call_info, NULL,
353                                                 ELM_GENLIST_ITEM_NONE,
354                                                 _on_item_clicked,
355                                                 call_info->line_id);
356                                 elm_genlist_item_show
357                                         (it, ELM_GENLIST_ITEM_SCROLLTO_IN);
358                                 call_info->it_missed = it;
359                                 call_info->history = history;
360                                 _history_time_updater_start(history);
361                         }
362                 }
363         }
364
365         history->calls->dirty = EINA_TRUE;
366         _history_call_log_save(history);
367
368         if (call_info->it_all)
369                 elm_genlist_item_update(call_info->it_all);
370         else {
371                 it = elm_genlist_item_prepend(history->genlist_all,
372                                                 history->itc,
373                                                 call_info, NULL,
374                                                 ELM_GENLIST_ITEM_NONE,
375                                                 _on_item_clicked,
376                                                 call_info->line_id);
377                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN);
378                 call_info->it_all = it;
379                 call_info->history = history;
380                 _history_time_updater_start(history);
381         }
382 }
383
384 static void _on_contact_del(void *data, const Contact_Info *contact __UNUSED__)
385 {
386         Call_Info *call_info = data;
387         call_info->contact = NULL;
388         call_info->contact_type = NULL;
389         call_info->contact_last = 0.0;
390 }
391
392 static void _on_contact_changed(void *data, Contact_Info *contact)
393 {
394         Call_Info *call_info = data;
395
396         if (contact_info_number_check(contact, call_info->line_id))
397                 goto update;
398
399         contact_info_on_del_callback_del(contact, _on_contact_del, call_info);
400         contact_info_on_changed_callback_del(contact, _on_contact_changed,
401                                                 call_info);
402
403         call_info->contact = NULL;
404         call_info->contact_type = NULL;
405         call_info->contact_last = 0.0;
406
407 update:
408         if (call_info->it_all)
409                 elm_genlist_item_update(call_info->it_all);
410         if (call_info->it_missed)
411                 elm_genlist_item_update(call_info->it_missed);
412 }
413
414 static void _call_info_free(Call_Info *call_info)
415 {
416         if (call_info->contact) {
417                 Contact_Info *contact = (Contact_Info *)call_info->contact;
418                 contact_info_on_del_callback_del(contact, _on_contact_del,
419                                                         call_info);
420                 contact_info_on_changed_callback_del(contact,
421                                                         _on_contact_changed,
422                                                         call_info);
423         }
424
425         eina_stringshare_del(call_info->line_id);
426         eina_stringshare_del(call_info->name);
427         free(call_info);
428 }
429
430 static void _on_del(void *data, Evas *e __UNUSED__,
431                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
432 {
433         History *history = data;
434         Call_Info *call_info;
435
436         if (history->updater)
437                 ecore_poller_del(history->updater);
438
439         if (history->calls->dirty)
440                 _history_call_log_save(history);
441
442         ofono_call_removed_cb_del(callback_node_call_removed);
443         ofono_call_changed_cb_del(callback_node_call_changed);
444         eet_data_descriptor_free(history->edd);
445         eet_data_descriptor_free(history->edd_list);
446         EINA_LIST_FREE(history->calls->list, call_info)
447                 _call_info_free(call_info);
448         free(history->calls);
449         elm_genlist_item_class_free(history->itc);
450         free(history->path);
451         free(history->bkp);
452         free(history);
453         ecore_file_shutdown();
454         eet_shutdown();
455 }
456
457 static void _on_hide(void *data, Evas *e __UNUSED__,
458                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
459 {
460         History *history = data;
461         DBG("history became hidden");
462         _history_time_updater_stop(history);
463 }
464
465 static void _on_show(void *data, Evas *e __UNUSED__,
466                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
467 {
468         History *history = data;
469         DBG("history became visible");
470         _history_time_updater_start(history);
471 }
472
473 static void _on_win_focus_out(void *data, Evas *e __UNUSED__,
474                                 void *event_info __UNUSED__)
475 {
476         History *history = data;
477         DBG("window is unfocused");
478         _history_time_updater_stop(history);
479 }
480
481 static void _on_win_focus_in(void *data, Evas *e __UNUSED__,
482                                 void *event_info __UNUSED__)
483 {
484         History *history = data;
485         DBG("window is focused");
486         _history_time_updater_start(history);
487 }
488
489 static void _history_call_info_descriptor_init(Eet_Data_Descriptor **edd,
490                                                 Eet_Data_Descriptor **edd_list)
491 {
492         Eet_Data_Descriptor_Class eddc;
493
494         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info);
495         *edd = eet_data_descriptor_stream_new(&eddc);
496
497         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info_List);
498         *edd_list = eet_data_descriptor_stream_new(&eddc);
499
500         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
501                                         "completed", completed, EET_T_UCHAR);
502         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
503                                         "incoming", incoming, EET_T_UCHAR);
504
505         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
506                                         "start_time", start_time, EET_T_LONG_LONG);
507         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
508                                         "end_time", end_time, EET_T_LONG_LONG);
509         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
510                                         "line_id", line_id, EET_T_STRING);
511         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
512                                         "name", name, EET_T_STRING);
513
514         EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Call_Info_List, "list", list,
515                                         *edd);
516 }
517
518 static void _history_call_log_read(History *history)
519 {
520         Call_Info *call_info;
521         Eina_List *l;
522         Eet_File *efile;
523         Call_Info_List *calls = NULL;
524         Elm_Object_Item *it;
525
526         efile = eet_open(history->path, EET_FILE_MODE_READ);
527
528         if (efile) {
529                 calls = eet_data_read(efile, history->edd_list, HISTORY_ENTRY);
530                 eet_close(efile);
531         }
532
533         if (!calls) {
534                 efile = eet_open(history->bkp, EET_FILE_MODE_READ);
535                 if (efile) {
536                         calls = eet_data_read(efile, history->edd_list,
537                                                 HISTORY_ENTRY);
538                         eet_close(efile);
539                 }
540         }
541
542         if (!calls)
543                 calls = calloc(1, sizeof(Call_Info_List));
544
545         history->calls = calls;
546         EINA_SAFETY_ON_NULL_RETURN(history->calls);
547
548         EINA_LIST_FOREACH(history->calls->list, l, call_info) {
549                 it = elm_genlist_item_append(history->genlist_all,
550                                                 history->itc,
551                                                 call_info, NULL,
552                                                 ELM_GENLIST_ITEM_NONE,
553                                                 _on_item_clicked,
554                                                 call_info->line_id);
555                 call_info->it_all = it;
556                 call_info->history = history;
557
558                 if (call_info->completed)
559                         continue;
560
561                 it = elm_genlist_item_append(history->genlist_missed,
562                                                 history->itc, call_info, NULL,
563                                                 ELM_GENLIST_ITEM_NONE,
564                                                 _on_item_clicked,
565                                                 call_info->line_id);
566                 call_info->it_missed = it;
567                 call_info->history = history;
568         }
569
570         it = elm_genlist_first_item_get(history->genlist_all);
571         if (it)
572                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
573
574         it = elm_genlist_first_item_get(history->genlist_missed);
575         if (it)
576                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
577 }
578
579 static void _history_call_info_del(Call_Info *call_info)
580 {
581         History *ctx = call_info->history;
582
583         EINA_SAFETY_ON_NULL_RETURN(ctx);
584
585         call_info->call = NULL;
586         if (call_info->it_all)
587                 elm_object_item_del(call_info->it_all);
588         if (call_info->it_missed)
589                 elm_object_item_del(call_info->it_missed);
590
591         ctx->calls->list = eina_list_remove(ctx->calls->list, call_info);
592         ctx->calls->dirty = EINA_TRUE;
593         _history_call_log_save(ctx);
594
595         if ((!ctx->calls->list) && (ctx->updater)) {
596                 ecore_poller_del(ctx->updater);
597                 ctx->updater = NULL;
598         }
599
600         _call_info_free(call_info);
601 }
602
603 static void _history_clear_do(void *data, Evas_Object *obj __UNUSED__,
604                                 void *event_info __UNUSED__)
605 {
606         History *ctx = data;
607         Call_Info *call_info;
608
609         DBG("ctx=%p, deleting %u entries",
610                 ctx, eina_list_count(ctx->calls->list));
611
612         evas_object_del(ctx->clear_popup);
613         ctx->clear_popup = NULL;
614
615         elm_genlist_clear(ctx->genlist_all);
616         elm_genlist_clear(ctx->genlist_missed);
617
618         EINA_LIST_FREE(ctx->calls->list, call_info)
619                 _call_info_free(call_info);
620
621         ctx->calls->dirty = EINA_TRUE;
622         _history_call_log_save(ctx);
623
624         if (ctx->updater) {
625                 ecore_poller_del(ctx->updater);
626                 ctx->updater = NULL;
627         }
628
629         elm_object_signal_emit(ctx->self, "toggle,off,edit", "gui");
630 }
631
632 static void _history_clear_cancel(void *data, Evas_Object *obj __UNUSED__,
633                                         void *event_info __UNUSED__)
634 {
635         History *ctx = data;
636
637         DBG("ctx=%p", ctx);
638
639         evas_object_del(ctx->clear_popup);
640         ctx->clear_popup = NULL;
641 }
642
643 static void _history_clear(History *ctx)
644 {
645         Evas_Object *p;
646
647         EINA_SAFETY_ON_TRUE_RETURN(ctx->clear_popup != NULL);
648
649         ctx->clear_popup = p = gui_simple_popup("Clear History",
650                                 "Do you want to clear all history entries?");
651
652         gui_simple_popup_buttons_set(p,
653                                         "Dismiss",
654                                         "dialer",
655                                         _history_clear_cancel,
656                                         "Yes, Clear",
657                                         "dialer-caution",
658                                         _history_clear_do,
659                                         ctx);
660 }
661
662 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
663                                 const char *part)
664 {
665         Call_Info *call_info = data;
666
667         if (strncmp(part, "text.call", strlen("text.call")))
668                 return NULL;
669
670         part += strlen("text.call.");
671
672         if (!call_info->contact) {
673                 double now = ecore_loop_time_get();
674                 double diff = now - call_info->contact_last;
675                 if (diff > CONTACT_LAST_THRESHOLD) {
676                         Contact_Info *contact = gui_contact_search(
677                                 call_info->line_id, &(call_info->contact_type));
678
679                         call_info->contact_last = now;
680                         call_info->contact = contact;
681                         if (contact) {
682                                 contact_info_on_del_callback_add(
683                                         contact, _on_contact_del, call_info);
684                                 contact_info_on_changed_callback_add(
685                                         contact, _on_contact_changed,
686                                         call_info);
687                         }
688                 }
689         }
690
691         if (!strcmp(part, "name")) {
692                 if (!call_info->contact)
693                         return strdup(call_info->line_id);
694                 return strdup(contact_info_full_name_get(call_info->contact));
695         }
696
697         if (!strcmp(part, "time")) {
698                 if ((call_info->completed) && (call_info->end_time))
699                         return date_format(call_info->end_time);
700                 return date_format(call_info->start_time);
701         }
702
703         if (!strcmp(part, "type")) {
704                 if (!call_info->contact_type)
705                         return strdup("Unknown");
706                 return strdup(call_info->contact_type);
707         }
708
709         ERR("Unexpected text part: %s", part);
710         return NULL;
711 }
712
713
714 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
715                                         const char  *part)
716 {
717         Call_Info *call_info = data;
718
719         if (!strcmp(part, "missed"))
720                 return !call_info->completed;
721         else if (!strcmp(part, "completed"))
722                 return call_info->completed;
723         else if (!strcmp(part, "outgoing"))
724                 return !call_info->incoming;
725         else if (!strcmp(part, "incoming"))
726                 return call_info->incoming;
727
728         ERR("Unexpected state part: %s", part);
729         return EINA_FALSE;
730 }
731
732 static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
733                         const char *emission, const char *source __UNUSED__)
734 {
735         History *ctx = data;
736
737         EINA_SAFETY_ON_NULL_RETURN(emission);
738         emission += strlen("clicked,");
739
740         DBG("ctx=%p, signal: %s", ctx, emission);
741
742         if (!strcmp(emission, "all"))
743                 elm_object_signal_emit(obj, "show,all", "gui");
744         else if (!strcmp(emission, "missed"))
745                 elm_object_signal_emit(obj, "show,missed", "gui");
746         else if (!strcmp(emission, "clear"))
747                 _history_clear(ctx);
748         else if (!strcmp(emission, "edit")) {
749                 elm_object_signal_emit(obj, "toggle,on,edit", "gui");
750                 elm_genlist_decorate_mode_set(ctx->genlist_all, EINA_TRUE);
751                 elm_genlist_decorate_mode_set(ctx->genlist_missed, EINA_TRUE);
752         } else if (!strcmp(emission, "edit,done")) {
753                 elm_object_signal_emit(obj, "toggle,off,edit", "gui");
754                 elm_genlist_decorate_mode_set(ctx->genlist_all, EINA_FALSE);
755                 elm_genlist_decorate_mode_set(ctx->genlist_missed, EINA_FALSE);
756         }
757 }
758
759 static void _on_more_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
760                                 const char *emission __UNUSED__,
761                                 const char *source __UNUSED__)
762 {
763         DBG("TODO");
764 }
765
766 static void _on_del_clicked(void *data, Evas_Object *obj __UNUSED__,
767                                 void *event_info __UNUSED__)
768 {
769         Call_Info *call_info = data;
770         DBG("call_info=%p, items all=%p missed=%p",
771                 call_info, call_info->it_all, call_info->it_missed);
772         _history_call_info_del(call_info);
773 }
774
775 static Evas_Object *_item_content_get(void *data, Evas_Object *obj,
776                                         const char *part)
777 {
778         Evas_Object *btn = NULL;
779
780         if (strcmp(part, "call.swallow.more") == 0) {
781                 btn = gui_layout_add(obj, "history/img");
782                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
783                 elm_object_signal_callback_add(btn, "clicked,more", "gui",
784                                                 _on_more_clicked, NULL);
785                 evas_object_propagate_events_set(btn, EINA_FALSE);
786         } else if (strcmp(part, "call.swallow.delete") == 0) {
787                 btn = elm_button_add(obj);
788                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
789                 elm_object_style_set(btn, "history-delete");
790                 elm_object_text_set(btn, "delete");
791                 evas_object_smart_callback_add(btn, "clicked", _on_del_clicked,
792                                                 data);
793                 evas_object_propagate_events_set(btn, EINA_FALSE);
794         } else
795                 ERR("unknown content part '%s'", part);
796
797         return btn;
798 }
799
800 static void _on_list_slide_enter(void *data __UNUSED__,
801                                         Evas_Object *obj,
802                                         void *event_info)
803 {
804         Elm_Object_Item *it = elm_genlist_decorated_item_get(obj);
805         DBG("cancel decorated item=%p", it);
806         if (it)
807                 elm_genlist_item_decorate_mode_set(it, "slide", EINA_FALSE);
808
809         it = event_info;
810         EINA_SAFETY_ON_NULL_RETURN(it);
811         DBG("it=%p", it);
812         elm_genlist_item_decorate_mode_set(it, "slide", EINA_TRUE);
813 }
814
815 static void _on_list_slide_cancel(void *data __UNUSED__,
816                                         Evas_Object *obj,
817                                         void *event_info __UNUSED__)
818 {
819         Elm_Object_Item *it = elm_genlist_decorated_item_get(obj);
820         DBG("it=%p", it);
821         if (it)
822                 elm_genlist_item_decorate_mode_set(it, "slide", EINA_FALSE);
823 }
824
825 Evas_Object *history_add(Evas_Object *parent)
826 {
827         int r;
828         History *history;
829         Evas *e;
830         const char *config_path;
831         char *path, base_dir[PATH_MAX];
832         Elm_Genlist_Item_Class *itc;
833         Evas_Object *obj, *genlist_all, *genlist_missed;
834
835         eet_init();
836         ecore_file_init();
837         history = calloc(1, sizeof(History));
838         EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
839
840         history->self = obj = gui_layout_add(parent, "history_bg");
841         EINA_SAFETY_ON_NULL_GOTO(obj, err_layout);
842
843         genlist_all = elm_genlist_add(obj);
844         EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
845         elm_object_style_set(genlist_all, "history");
846         elm_genlist_homogeneous_set(genlist_all, EINA_TRUE);
847
848         evas_object_smart_callback_add(genlist_all, "drag,start,right",
849                                         _on_list_slide_enter, history);
850         evas_object_smart_callback_add(genlist_all, "drag,start,left",
851                                         _on_list_slide_cancel, history);
852         evas_object_smart_callback_add(genlist_all, "drag,start,down",
853                                         _on_list_slide_cancel, history);
854         evas_object_smart_callback_add(genlist_all, "drag,start,up",
855                                         _on_list_slide_cancel, history);
856
857         genlist_missed = elm_genlist_add(obj);
858         EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
859         elm_object_style_set(genlist_missed, "history");
860         elm_genlist_homogeneous_set(genlist_missed, EINA_TRUE);
861
862         evas_object_smart_callback_add(genlist_missed, "drag,start,right",
863                                         _on_list_slide_enter, history);
864         evas_object_smart_callback_add(genlist_missed, "drag,start,left",
865                                         _on_list_slide_cancel, history);
866         evas_object_smart_callback_add(genlist_missed, "drag,start,down",
867                                         _on_list_slide_cancel, history);
868         evas_object_smart_callback_add(genlist_missed, "drag,start,up",
869                                         _on_list_slide_cancel, history);
870
871         itc = elm_genlist_item_class_new();
872         EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
873         itc->item_style = "history";
874         itc->func.text_get = _item_label_get;
875         itc->func.content_get = _item_content_get;
876         itc->func.state_get = _item_state_get;
877         itc->func.del = NULL;
878         itc->decorate_all_item_style = "history-delete";
879         itc->decorate_item_style = "history-delete";
880         history->genlist_all = genlist_all;
881         history->genlist_missed = genlist_missed;
882         history->itc = itc;
883
884         elm_object_part_content_set(obj, "elm.swallow.all", genlist_all);
885         elm_object_part_content_set(obj, "elm.swallow.missed", genlist_missed);
886         elm_object_signal_emit(obj, "show,all", "gui");
887         elm_object_signal_callback_add(obj, "clicked,*", "gui",
888                                         _on_clicked, history);
889
890         config_path = efreet_config_home_get();
891         snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
892                         PACKAGE_NAME);
893         ecore_file_mkpath(base_dir);
894         r = asprintf(&path,  "%s/%s/history.eet", config_path, PACKAGE_NAME);
895
896         if (r < 0)
897                 goto err_item_class;
898
899         history->path = path;
900         r = asprintf(&path,  "%s/%s/history.eet.bkp", config_path,
901                         PACKAGE_NAME);
902
903         if (r < 0)
904                 goto err_path;
905
906         history->bkp = path;
907
908         _history_call_info_descriptor_init(&history->edd, &history->edd_list);
909         _history_call_log_read(history);
910         EINA_SAFETY_ON_NULL_GOTO(history->calls, err_log_read);
911         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
912                                         history);
913         evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide,
914                                         history);
915         evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show,
916                                         history);
917
918         e = evas_object_evas_get(obj);
919         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_OUT,
920                                 _on_win_focus_out, history);
921         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_IN,
922                                 _on_win_focus_in, history);
923
924         callback_node_call_changed =
925                 ofono_call_changed_cb_add(_history_call_changed, history);
926         callback_node_call_removed =
927                 ofono_call_removed_cb_add(_history_call_removed, history);
928
929         return obj;
930
931 err_log_read:
932         free(history->bkp);
933         eet_data_descriptor_free(history->edd);
934         eet_data_descriptor_free(history->edd_list);
935 err_path:
936         free(history->path);
937 err_item_class:
938         elm_genlist_item_class_free(itc);
939 err_object_new:
940         free(obj);
941 err_layout:
942         free(history);
943         ecore_file_shutdown();
944         eet_shutdown();
945         return NULL;
946 }