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