Several changes in the contacts layout tab and accross the App.
[profile/ivi/lemolo.git] / dialer / callscreen.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include <Elementary.h>
5
6 #include "log.h"
7 #include "gui.h"
8 #include "ofono.h"
9 #include "util.h"
10
11 typedef struct _Callscreen
12 {
13         Evas_Object *self;
14         Evas_Object *gui_activecall; /* for gui.h */
15         struct {
16                 Evas_Object *sc;
17                 Evas_Object *bx;
18                 Eina_List *calls;
19         } multiparty;
20         struct {
21                 OFono_Call *active;
22                 OFono_Call *waiting;
23                 OFono_Call *held;
24                 Eina_List *list;
25         } calls;
26         OFono_Call_State last_state;
27         Ecore_Timer *elapsed_updater;
28         struct {
29                 Eina_Strbuf *todo;
30                 OFono_Pending *pending;
31         } tones;
32         struct {
33                 const void *call;
34                 const char *number;
35                 Evas_Object *popup;
36         } disconnected;
37 } Callscreen;
38
39 static void _on_active_call_clicked(void *data __UNUSED__,
40                                         Evas_Object *o __UNUSED__,
41                                         const char *emission __UNUSED__,
42                                         const char *source __UNUSED__)
43 {
44         gui_call_enter();
45 }
46
47 static OFono_Callback_List_Modem_Node *callback_node_modem_changed = NULL;
48
49 static OFono_Callback_List_Call_Node *callback_node_call_added = NULL;
50 static OFono_Callback_List_Call_Node *callback_node_call_removed = NULL;
51 static OFono_Callback_List_Call_Node *callback_node_call_changed = NULL;
52
53 static OFono_Callback_List_Call_Disconnected_Node
54 *callback_node_call_disconnected = NULL;
55
56 static char *_call_name_or_id(const OFono_Call *call);
57 static char *_call_type_get(const OFono_Call *call);
58
59 static void _on_mp_hangup(void *data, Evas_Object *o __UNUSED__,
60                                 const char *emission __UNUSED__,
61                                 const char *source __UNUSED__)
62 {
63         OFono_Call *call = data;
64         DBG("User ask hangup of multiparty call=%p", call);
65         ofono_call_hangup(call, NULL, NULL);
66 }
67
68 static void _on_mp_pvt_reply(void *data, OFono_Error err)
69 {
70         Callscreen *ctx = data;
71
72         DBG("PrivateChat: err=%d", err);
73
74         if (err == OFONO_ERROR_NONE)
75                 elm_object_signal_emit(ctx->self, "multiparty,private", "call");
76 }
77
78 static void _on_mp_pvt(void *data, Evas_Object *o,
79                                 const char *emission __UNUSED__,
80                                 const char *source __UNUSED__)
81 {
82         Callscreen *ctx = evas_object_data_get(o, "callscreen.ctx");
83         OFono_Call *call = data;
84         DBG("User ask private chat of multiparty call=%p", call);
85         ofono_private_chat(call, _on_mp_pvt_reply, ctx);
86 }
87
88 static void _on_raise(void *data __UNUSED__, Evas_Object *o,
89                                 const char *emission __UNUSED__,
90                                 const char *source __UNUSED__)
91 {
92         evas_object_raise(o);
93 }
94
95 static void _multiparty_update(Callscreen *ctx)
96 {
97         Eina_List *new = NULL, *old = NULL;
98         const Eina_List *n1, *n2;
99         OFono_Call *c;
100         Evas_Object *it;
101
102         EINA_LIST_FOREACH(ctx->calls.list, n1, c) {
103                 if (ofono_call_multiparty_get(c))
104                         new = eina_list_append(new, c);
105         }
106
107         old = ctx->multiparty.calls;
108         if (eina_list_count(new) != eina_list_count(old))
109                 goto repopulate;
110
111         for (n1 = new, n2 = old; n1 && n2; n1 = n1->next, n2 = n2->next) {
112                 if (n1->data != n2->data)
113                         break;
114         }
115
116         if (n1)
117                 goto repopulate;
118
119         eina_list_free(new);
120         return;
121
122 repopulate:
123         eina_list_free(ctx->multiparty.calls);
124         ctx->multiparty.calls = new;
125
126         elm_box_clear(ctx->multiparty.bx);
127
128         if (!new) {
129                 elm_object_signal_emit(ctx->self, "hide,multiparty-details",
130                                         "call");
131                 return;
132         }
133
134         EINA_LIST_FOREACH(new, n1, c) {
135                 char *name, *number, *type;
136
137                 name = _call_name_or_id(c);
138                 type = _call_type_get(c);
139                 number = phone_format(ofono_call_line_id_get(c));
140
141                 it = gui_layout_add(ctx->multiparty.bx, "multiparty-details");
142                 evas_object_size_hint_align_set(it,
143                                                 EVAS_HINT_FILL, EVAS_HINT_FILL);
144                 evas_object_show(it);
145
146                 elm_object_part_text_set(it, "elm.text.name", name);
147                 elm_object_part_text_set(it, "elm.text.number", number);
148                 elm_object_part_text_set(it, "elm.text.type", type);
149
150                 if (strcmp(name, number) == 0)
151                         elm_object_signal_emit(it, "hide,name", "call");
152                 else
153                         elm_object_signal_emit(it, "show,name", "call");
154
155                 elm_object_signal_callback_add(it, "clicked,hangup", "call",
156                                                 _on_mp_hangup, c);
157                 elm_object_signal_callback_add(it, "clicked,private", "call",
158                                                 _on_mp_pvt, c);
159                 elm_object_signal_callback_add(it, "raise", "call",
160                                                 _on_raise, NULL);
161
162                 evas_object_data_set(it, "callscreen.ctx", ctx);
163                 elm_box_pack_end(ctx->multiparty.bx, it);
164
165                 free(name);
166                 free(number);
167                 free(type);
168         }
169         elm_object_signal_emit(ctx->self, "show,multiparty-details", "call");
170 }
171
172 static void _multiparty_private_available_update(Callscreen *ctx)
173 {
174         Eina_List *lst = elm_box_children_get(ctx->multiparty.bx);
175         const char *sig = ctx->calls.held ? "hide,private" : "show,private";
176         Evas_Object *it;
177
178         EINA_LIST_FREE(lst, it)
179                 elm_object_signal_emit(it, sig, "call");
180 }
181
182 static void _calls_update(Callscreen *ctx)
183 {
184         const Eina_List *n;
185         OFono_Call *c, *found = NULL, *waiting = NULL, *held = NULL;
186         OFono_Call_State found_state = OFONO_CALL_STATE_DISCONNECTED;
187         static const int state_priority[] = {
188                 [OFONO_CALL_STATE_DISCONNECTED] = 0,
189                 [OFONO_CALL_STATE_ACTIVE] = 6,
190                 [OFONO_CALL_STATE_HELD] = 3,
191                 [OFONO_CALL_STATE_DIALING] = 5,
192                 [OFONO_CALL_STATE_ALERTING] = 4,
193                 [OFONO_CALL_STATE_INCOMING] = 2,
194                 [OFONO_CALL_STATE_WAITING] = 1
195         };
196
197         if (ctx->calls.active) {
198                 OFono_Call_State s = ofono_call_state_get(ctx->calls.active);
199                 if (s != OFONO_CALL_STATE_ACTIVE) {
200                         ctx->calls.active = NULL;
201                         ctx->last_state = 0;
202                 }
203         }
204         if (ctx->calls.waiting) {
205                 OFono_Call_State s = ofono_call_state_get(ctx->calls.waiting);
206                 if (s != OFONO_CALL_STATE_WAITING)
207                         ctx->calls.waiting = NULL;
208         }
209         if (ctx->calls.held) {
210                 OFono_Call_State s = ofono_call_state_get(ctx->calls.held);
211                 if (s != OFONO_CALL_STATE_HELD)
212                         ctx->calls.held = NULL;
213         }
214
215         _multiparty_update(ctx);
216
217         if (ctx->calls.active && ctx->calls.waiting && ctx->calls.held)
218                 return;
219
220         EINA_LIST_FOREACH(ctx->calls.list, n, c) {
221                 OFono_Call_State state = ofono_call_state_get(c);
222
223                 DBG("compare %p (%d) to %p (%d)", found, found_state,
224                         c, state);
225                 if (state_priority[state] > state_priority[found_state]) {
226                         found_state = state;
227                         found = c;
228                 }
229                 if ((!waiting) && (state == OFONO_CALL_STATE_WAITING))
230                         waiting = c;
231                 if ((!held) && (state == OFONO_CALL_STATE_HELD))
232                         held = c;
233         }
234
235         DBG("found=%p, state=%d, waiting=%p, held=%p",
236                 found, found_state, waiting, held);
237         if (!found)
238                 return;
239
240         if (!ctx->calls.active) {
241                 ctx->calls.active = found;
242                 ctx->last_state = 0;
243         }
244         if (!ctx->calls.waiting)
245                 ctx->calls.waiting = waiting;
246         if (!ctx->calls.held)
247                 ctx->calls.held = held;
248
249         gui_call_enter();
250 }
251
252 static void _call_disconnected_done(Callscreen *ctx, const char *reason)
253 {
254         _calls_update(ctx);
255
256         if (!ctx->calls.list) {
257                 if (ctx->gui_activecall) {
258                         gui_activecall_set(NULL);
259                         evas_object_del(ctx->gui_activecall);
260                         ctx->gui_activecall = NULL;
261                 }
262                 gui_call_exit();
263         } else {
264                 if (strcmp(reason, "local") == 0) {
265                         /* Bug #11 says this shouldn't be done.
266                          *
267                          * TODO: talk to ofono devs and see what to do,
268                          * maybe introuce ReleaseAndSwap()
269                          */
270 #if 0
271                         /* If there is a held call and active is
272                          * hangup we're left with held but no active,
273                          * which is strange.
274                          *
275                          * Just make the held active by calling
276                          * SwapCalls.
277                          */
278                         if (ctx->calls.active == ctx->disconnected.call &&
279                                 ctx->calls.held != ctx->disconnected.call) {
280                                 INF("User disconnected and left held call. "
281                                         "Automatically activate it.");
282
283                                 /* TODO: sound to notify user */
284                                 ofono_swap_calls(NULL, NULL);
285                         }
286 #endif
287                 }
288         }
289         ctx->disconnected.call = NULL;
290 }
291
292 static void _popup_close(void *data, Evas_Object *o __UNUSED__, void *event __UNUSED__)
293 {
294         Callscreen *ctx = data;
295
296         evas_object_del(ctx->disconnected.popup);
297         ctx->disconnected.popup = NULL;
298
299         eina_stringshare_replace(&ctx->disconnected.number, NULL);
300
301         _call_disconnected_done(ctx, "network");
302 }
303
304 static void _popup_redial(void *data, Evas_Object *o __UNUSED__, void *event __UNUSED__)
305 {
306         Callscreen *ctx = data;
307
308         ofono_dial(ctx->disconnected.number, NULL, NULL, NULL);
309         _popup_close(ctx, NULL, NULL);
310 }
311
312 static void _call_disconnected_show(Callscreen *ctx, OFono_Call *c,
313                                         const char *reason)
314 {
315         Evas_Object *p, *bt;
316         const char *number, *title;
317         char msg[1024];
318
319         DBG("ctx=%p, active=%p, held=%p, waiting=%p, previous=%s, "
320                 "disconnected=%p (%s)",
321                 ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
322                 ctx->disconnected.number, c, reason);
323
324         if (ctx->calls.waiting == c) {
325                 ctx->calls.waiting = NULL;
326                 elm_object_part_text_set(ctx->self, "elm.text.waiting", "");
327                 elm_object_signal_emit(ctx->self, "hide,waiting", "call");
328                 goto done;
329         }
330         if (ctx->calls.held == c) {
331                 ctx->calls.held = NULL;
332                 if (ofono_call_multiparty_get(c))
333                         goto done;
334                 elm_object_part_text_set(ctx->self, "elm.text.held", "");
335                 elm_object_signal_emit(ctx->self, "hide,held", "call");
336                 _multiparty_private_available_update(ctx);
337                 goto done;
338         }
339
340         if ((ctx->calls.active) && (ctx->calls.active != c))
341                 goto done;
342         ctx->calls.active = NULL;
343         ctx->last_state = 0;
344         ctx->disconnected.call = c;
345
346         elm_object_signal_emit(ctx->self, "active,disconnected", "call");
347
348         if ((strcmp(reason, "local") == 0) || (strcmp(reason, "remote") == 0))
349                 goto done;
350
351         number = ofono_call_line_id_get(c);
352         if ((!number) || (number[0] == '\0'))
353                 goto done;
354
355         if (ctx->disconnected.number)
356                 goto done;
357
358         if (strcmp(reason, "network") == 0)
359                 title = "Network Disconnected!";
360         else
361                 title = "Disconnected!";
362
363         snprintf(msg, sizeof(msg), "Try to redial %s", number);
364
365         eina_stringshare_replace(&ctx->disconnected.number, number);
366
367         ctx->disconnected.popup = p = elm_popup_add(ctx->self);
368         evas_object_size_hint_weight_set(p, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
369         elm_object_part_text_set(p, "title,text", title);
370         elm_object_text_set(p, msg);
371
372         bt = elm_button_add(p);
373         elm_object_text_set(bt, "Close");
374         elm_object_part_content_set(p, "button1", bt);
375         evas_object_smart_callback_add(bt, "clicked", _popup_close, ctx);
376
377         bt = elm_button_add(p);
378         elm_object_text_set(bt, "Redial");
379         elm_object_part_content_set(p, "button2", bt);
380         evas_object_smart_callback_add(bt, "clicked", _popup_redial, ctx);
381
382         /* TODO: sound to notify user */
383
384         evas_object_show(p);
385
386         return;
387 done:
388         _call_disconnected_done(ctx, reason);
389 }
390
391 static void _tones_send_reply(void *data, OFono_Error err)
392 {
393         Callscreen *ctx = data;
394
395         if (err)
396                 ERR("Failed to send tones: %d", err);
397
398         ctx->tones.pending = NULL;
399         if (eina_strbuf_length_get(ctx->tones.todo) > 0) {
400                 const char *tones = eina_strbuf_string_get(ctx->tones.todo);
401
402                 DBG("Send pending tones: %s", tones);
403                 ctx->tones.pending = ofono_tones_send(
404                         tones, _tones_send_reply, ctx);
405                 eina_strbuf_reset(ctx->tones.todo);
406         }
407 }
408
409 static void _on_pressed(void *data, Evas_Object *obj __UNUSED__,
410                         const char *emission, const char *source __UNUSED__)
411 {
412         Callscreen *ctx = data;
413         DBG("ctx=%p, active=%p, held=%p, waiting=%p, signal: %s",
414                 ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
415                 emission);
416
417         EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "pressed,"));
418         emission += strlen("pressed,");
419
420 }
421
422 static void _on_released(void *data, Evas_Object *obj __UNUSED__,
423                                 const char *emission,
424                                 const char *source __UNUSED__)
425 {
426         Callscreen *ctx = data;
427         DBG("ctx=%p, active=%p, held=%p, waiting=%p, signal: %s",
428                 ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
429                 emission);
430
431         EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "released,"));
432         emission += strlen("released,");
433
434 }
435
436 static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
437                         const char *emission, const char *source __UNUSED__)
438 {
439         Callscreen *ctx = data;
440         const char *dtmf = NULL;
441         DBG("ctx=%p, active=%p, held=%p, waiting=%p, signal: %s",
442                 ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
443                 emission);
444
445         EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "clicked,"));
446         emission += strlen("clicked,");
447
448         if ((emission[0] >= '0') && (emission[0] <= '9'))
449                 dtmf = emission;
450         else if (strcmp(emission, "star") == 0)
451                 dtmf = "*";
452         else if (strcmp(emission, "hash") == 0)
453                 dtmf = "#";
454
455         if (dtmf) {
456                 if (!ctx->tones.pending)
457                         ctx->tones.pending = ofono_tones_send(
458                                 dtmf, _tones_send_reply, ctx);
459                 else
460                         eina_strbuf_append_char(ctx->tones.todo, dtmf[0]);
461                 return;
462         }
463
464         if (strcmp(emission, "hangup") == 0) {
465                 if (ctx->calls.active) {
466                         OFono_Call *c = ctx->calls.active;
467                         if (ofono_call_multiparty_get(c))
468                                 ofono_multiparty_hangup(NULL, NULL);
469                         else
470                                 ofono_call_hangup(c, NULL, NULL);
471                 }
472         } else if (strcmp(emission, "answer") == 0) {
473                 if (ctx->calls.active)
474                         ofono_call_answer(ctx->calls.active, NULL, NULL);
475         } else if (strcmp(emission, "mute") == 0) {
476                 Eina_Bool val = !ofono_mute_get();
477                 ofono_mute_set(val, NULL, NULL);
478         } else if (strcmp(emission, "speaker") == 0) {
479                 ERR("TODO - implement platform loudspeaker code");
480         } else if (strcmp(emission, "contacts") == 0) {
481                 ERR("TODO - implement access to contacts");
482         } else if (strcmp(emission, "add-call") == 0) {
483                 gui_call_exit();
484         } else if (strcmp(emission, "merge") == 0) {
485                 if (ctx->calls.held)
486                         ofono_multiparty_create(NULL, NULL);
487         } else if (strcmp(emission, "swap") == 0) {
488                 if (ctx->calls.held)
489                         ofono_swap_calls(NULL, NULL);
490         } else if (strcmp(emission, "waiting-hangup") == 0) {
491                 if (ctx->calls.waiting)
492                         ofono_call_hangup(ctx->calls.waiting, NULL, NULL);
493         } else if (strcmp(emission, "hangup-answer") == 0) {
494                 if (ctx->calls.waiting)
495                         ofono_release_and_answer(NULL, NULL);
496         } else if (strcmp(emission, "hold-answer") == 0) {
497                 if (ctx->calls.waiting)
498                         ofono_hold_and_answer(NULL, NULL);
499         }
500 }
501
502 static void _on_key_down(void *data, Evas *e __UNUSED__,
503                                 Evas_Object *o __UNUSED__,
504                                 void *event_info)
505 {
506         Callscreen *ctx = data;
507         Evas_Event_Key_Down *ev = event_info;
508         DBG("ctx=%p, key=%s (%s, %s)", ctx, ev->keyname, ev->key, ev->string);
509
510         if ((strcmp(ev->key, "minus") == 0) ||
511                 (strcmp(ev->key, "KP_Subtract") == 0) ||
512                 (strcmp(ev->key, "XF86AudioLowerVolume") == 0)) {
513                 unsigned char last, cur;
514
515                 last = cur = ofono_volume_speaker_get();
516                 if (cur < 10)
517                         cur = 0;
518                 else
519                         cur -= 10;
520
521                 if (last != cur)
522                         ofono_volume_speaker_set(cur, NULL, NULL);
523         } else if ((strcmp(ev->key, "plus") == 0) ||
524                         (strcmp(ev->key, "KP_Add") == 0) ||
525                         (strcmp(ev->key, "XF86AudioRaiseVolume") == 0)) {
526                 unsigned char last, cur;
527
528                 last = cur = ofono_volume_speaker_get();
529                 if (cur > 90)
530                         cur = 100;
531                 else
532                         cur += 10;
533
534                 if (last != cur)
535                         ofono_volume_speaker_set(cur, NULL, NULL);
536         }
537 }
538
539 static void _ofono_changed(void *data)
540 {
541         Callscreen *ctx = data;
542         Edje_Message_Float msgf;
543         Evas_Object *ed;
544         const char *sig;
545
546         if (!ctx->calls.list)
547                 return;
548
549         sig = ofono_mute_get() ? "toggle,on,mute" : "toggle,off,mute";
550         elm_object_signal_emit(ctx->self, sig, "call");
551
552         ed = elm_layout_edje_get(ctx->self);
553
554         msgf.val = (ofono_volume_speaker_get() / 100.0);
555         edje_object_message_send(ed, EDJE_MESSAGE_FLOAT, 1, &msgf);
556
557         msgf.val = (ofono_volume_microphone_get() / 100.0);
558         edje_object_message_send(ed, EDJE_MESSAGE_FLOAT, 2, &msgf);
559 }
560
561
562 static void _call_added(void *data, OFono_Call *c)
563 {
564         Callscreen *ctx = data;
565         DBG("ctx=%p, active=%p, held=%p, waiting=%p, added=%p",
566                 ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting, c);
567
568         ctx->calls.list = eina_list_append(ctx->calls.list, c);
569         if (!ctx->calls.active) {
570                 _calls_update(ctx);
571                 gui_call_enter();
572         }
573 }
574
575 static void _call_removed(void *data, OFono_Call *c)
576 {
577         Callscreen *ctx = data;
578         DBG("ctx=%p, active=%p, held=%p, waiting=%p, removed=%p",
579                 ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting, c);
580         ctx->calls.list = eina_list_remove(ctx->calls.list, c);
581         _call_disconnected_show(ctx, c, "local");
582 }
583
584 static Eina_Bool _on_elapsed_updater(void *data)
585 {
586         Callscreen *ctx = data;
587         double next, elapsed, start;
588         Edje_Message_Float msgf;
589         Evas_Object *ed, *activecall = ctx->gui_activecall;
590         char buf[128];
591
592         if (!ctx->calls.active)
593                 goto stop;
594
595         start = ofono_call_start_time_get(ctx->calls.active);
596         if (start < 0) {
597                 ERR("Unknown start time for call");
598                 goto stop;
599         }
600
601         elapsed = ecore_loop_time_get() - start;
602         if (elapsed < 0) {
603                 ERR("Time rewinded? %f - %f = %f", ecore_loop_time_get(), start,
604                         elapsed);
605                 goto stop;
606         }
607
608         ed = elm_layout_edje_get(ctx->self);
609         msgf.val = elapsed;
610         edje_object_message_send(ed, EDJE_MESSAGE_FLOAT, 3, &msgf);
611
612         if (elapsed > 3600)
613                 snprintf(buf, sizeof(buf), "%02d:%02d:%02d",
614                                 (int)elapsed / 3600,
615                                 (int)elapsed % 3600 / 60,
616                                 (int)elapsed % 60);
617         else
618                 snprintf(buf, sizeof(buf), "%02d:%02d",
619                                 (int)elapsed / 60,
620                                 (int)elapsed % 60);
621         elm_object_part_text_set(ctx->self, "elm.text.elapsed", buf);
622         if (activecall)
623                 elm_object_part_text_set(activecall, "elm.text.elapsed", buf);
624
625         next = 1.0 - (elapsed - (int)elapsed);
626         ctx->elapsed_updater = ecore_timer_add(next, _on_elapsed_updater, ctx);
627         return EINA_FALSE;
628
629 stop:
630         if (activecall) {
631                 elm_object_part_text_set(activecall, "elm.text.elapsed", "");
632                 elm_object_signal_emit(activecall, "hide,elapsed", "call");
633         }
634
635         elm_object_part_text_set(ctx->self, "elm.text.elapsed", "");
636         elm_object_signal_emit(ctx->self, "hide,elapsed", "call");
637         ctx->elapsed_updater = NULL;
638         return EINA_FALSE;
639 }
640
641 static char *_call_name_or_id(const OFono_Call *call)
642 {
643         const char *s = ofono_call_line_id_get(call);
644         Contact_Info *info = gui_contact_search(s, NULL);
645
646         if (info) {
647                 return strdup(contact_info_name_get(info));
648         }
649
650         return phone_format(ofono_call_line_id_get(call));
651 }
652
653 static char *_call_name_get(const Callscreen *ctx __UNUSED__,
654                                 const OFono_Call *call)
655 {
656         if (!ofono_call_multiparty_get(call))
657                 return _call_name_or_id(call);
658
659         return strdup("Conference");
660 }
661
662 static char *_call_type_get(const OFono_Call *call)
663 {
664         const char *type;
665         const char *s = ofono_call_line_id_get(call);
666         Contact_Info *info = gui_contact_search(s, &type);
667
668         if (info) {
669                 return strdup(type);
670         }
671
672         return NULL;
673 }
674
675 static void _call_changed(void *data, OFono_Call *c)
676 {
677         Callscreen *ctx = data;
678         OFono_Call_State state;
679         Eina_Bool was_waiting, was_held, is_held;
680         const char *status, *sig = "hide,answer";
681         char *contact, *type;
682
683         DBG("ctx=%p, active=%p, held=%p, waiting=%p, changed=%p",
684                 ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting, c);
685
686         was_waiting = !!ctx->calls.waiting;
687
688         was_held = (ctx->calls.held) && (ctx->calls.held != ctx->calls.active);
689
690         _calls_update(ctx);
691
692         if ((ctx->calls.waiting) && (!was_waiting)) {
693                 char buf[256];
694                 contact = _call_name_get(ctx, ctx->calls.waiting);
695                 snprintf(buf, sizeof(buf), "%s is waiting...", contact);
696                 elm_object_part_text_set(ctx->self, "elm.text.waiting", buf);
697                 elm_object_signal_emit(ctx->self, "show,waiting", "call");
698                 free(contact);
699         } else if ((!ctx->calls.waiting) && (was_waiting)) {
700                 elm_object_part_text_set(ctx->self, "elm.text.waiting", "");
701                 elm_object_signal_emit(ctx->self, "hide,waiting", "call");
702         }
703
704         is_held = (ctx->calls.held) && (ctx->calls.held != ctx->calls.active);
705
706         if ((is_held) && (!was_held)) {
707                 elm_object_signal_emit(ctx->self, "show,held", "call");
708                 _multiparty_private_available_update(ctx);
709         } else if ((!is_held) && (was_held)) {
710                 elm_object_part_text_set(ctx->self, "elm.text.held", "");
711                 elm_object_signal_emit(ctx->self, "hide,held", "call");
712                 _multiparty_private_available_update(ctx);
713         }
714
715         c = ctx->calls.active;
716         if (!c)
717                 return;
718
719         contact = _call_name_get(ctx, c);
720         type = _call_type_get(c);
721
722         state = ofono_call_state_get(c);
723         switch (state) {
724         case OFONO_CALL_STATE_DISCONNECTED:
725                 status = "Disconnected";
726                 break;
727         case OFONO_CALL_STATE_ACTIVE:
728                 status = "Active";
729                 break;
730         case OFONO_CALL_STATE_HELD:
731                 status = "Held";
732                 break;
733         case OFONO_CALL_STATE_DIALING:
734                 status = "Dialing...";
735                 break;
736         case OFONO_CALL_STATE_ALERTING:
737                 status = "Alerting...";
738                 break;
739         case OFONO_CALL_STATE_INCOMING:
740                 status = "Incoming...";
741                 sig = "show,answer";
742                 break;
743         case OFONO_CALL_STATE_WAITING:
744                 status = "Waiting...";
745                 break;
746         default:
747                 status = "?";
748         }
749
750         if (!ctx->gui_activecall) {
751                 ctx->gui_activecall = gui_layout_add(ctx->self, "activecall");
752                 elm_object_signal_callback_add(ctx->gui_activecall,
753                                                 "clicked", "call",
754                                                 _on_active_call_clicked, ctx);
755                 gui_activecall_set(ctx->gui_activecall);
756         }
757         elm_object_part_text_set(ctx->gui_activecall, "elm.text.name", contact);
758         if (type)
759                 elm_object_part_text_set(ctx->gui_activecall,
760                                                 "elm.text.phone.type", type);
761         elm_object_part_text_set(ctx->gui_activecall, "elm.text.status",
762                                         status);
763
764
765         elm_object_part_text_set(ctx->self, "elm.text.name", contact);
766         elm_object_part_text_set(ctx->self, "elm.text.phone.type", type);
767         free(contact);
768         free(type);
769
770         elm_object_part_text_set(ctx->self, "elm.text.status", status);
771         elm_object_signal_emit(ctx->self, sig, "call");
772
773         sig = ofono_call_multiparty_get(c) ? "show,multiparty" :
774                 "hide,multiparty";
775         elm_object_signal_emit(ctx->self, sig, "call");
776         elm_object_signal_emit(ctx->gui_activecall, sig, "call");
777
778         if (ctx->last_state != state) {
779                 Eina_Bool have_updater = !!ctx->elapsed_updater;
780                 Eina_Bool want_updater = EINA_FALSE;
781
782                 switch (state) {
783                 case OFONO_CALL_STATE_DISCONNECTED:
784                         sig = "state,disconnected";
785                         break;
786                 case OFONO_CALL_STATE_ACTIVE:
787                         sig = "state,active";
788                         want_updater = EINA_TRUE;
789                         break;
790                 case OFONO_CALL_STATE_HELD:
791                         sig = "state,held";
792                         break;
793                 case OFONO_CALL_STATE_DIALING:
794                         sig = "state,dialing";
795                         break;
796                 case OFONO_CALL_STATE_ALERTING:
797                         sig = "state,alerting";
798                         break;
799                 case OFONO_CALL_STATE_INCOMING:
800                         sig = "state,incoming";
801                         break;
802                 case OFONO_CALL_STATE_WAITING:
803                         sig = "state,waiting";
804                         break;
805                 default:
806                         sig = NULL;
807                 }
808                 if (sig) {
809                         elm_object_signal_emit(ctx->self, sig, "call");
810                         elm_object_signal_emit(ctx->gui_activecall,
811                                                 sig, "call");
812                 }
813                 ctx->last_state = state;
814
815                 sig = NULL;
816                 if (have_updater && !want_updater) {
817                         ecore_timer_del(ctx->elapsed_updater);
818                         ctx->elapsed_updater = NULL;
819                         sig = "hide,elapsed";
820                         elm_object_part_text_set(ctx->self, "elm.text.elapsed",
821                                                         "");
822                 } else if (want_updater) {
823                         if (!have_updater)
824                                 sig = "show,elapsed";
825                         else {
826                                 ecore_timer_del(ctx->elapsed_updater);
827                                 ctx->elapsed_updater = NULL;
828                         }
829                         _on_elapsed_updater(ctx);
830                 }
831                 if (sig) {
832                         elm_object_signal_emit(ctx->self, sig, "call");
833                         elm_object_signal_emit(ctx->gui_activecall,
834                                                 sig, "call");
835                 }
836         }
837
838         if (is_held) {
839                 contact = _call_name_get(ctx, ctx->calls.held);
840                 elm_object_part_text_set(ctx->self, "elm.text.held", contact);
841                 free(contact);
842
843                 if (ofono_call_multiparty_get(ctx->calls.held))
844                         sig = "show,held,multiparty";
845                 else
846                         sig = "hide,held,multiparty";
847                 elm_object_signal_emit(ctx->self, sig, "call");
848         }
849
850         sig = is_held ? "enable,merge" : "disable,merge";
851         elm_object_signal_emit(ctx->self, sig, "call");
852
853         sig = ctx->calls.held ? "enable,swap" : "disable,swap";
854         elm_object_signal_emit(ctx->self, sig, "call");
855
856         if (state == OFONO_CALL_STATE_DISCONNECTED)
857                 _call_disconnected_show(ctx, c, "local");
858
859         _ofono_changed(ctx);
860 }
861
862 static void _call_disconnected(void *data, OFono_Call *c, const char *reason)
863 {
864         Callscreen *ctx = data;
865         DBG("ctx=%p, active=%p, held=%p, waiting=%p, disconnected=%p (%s)",
866                 ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
867                 c, reason);
868
869         EINA_SAFETY_ON_NULL_RETURN(reason);
870         _call_disconnected_show(ctx, c, reason);
871 }
872
873 static void _on_del(void *data, Evas *e __UNUSED__,
874                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
875 {
876         Callscreen *ctx = data;
877
878         ofono_call_added_cb_del(callback_node_call_added);
879         ofono_call_removed_cb_del(callback_node_call_removed);
880         ofono_call_changed_cb_del(callback_node_call_changed);
881         ofono_call_disconnected_cb_del(callback_node_call_disconnected);
882         ofono_modem_changed_cb_del(callback_node_modem_changed);
883
884         eina_strbuf_free(ctx->tones.todo);
885         if (ctx->tones.pending)
886                 ofono_pending_cancel(ctx->tones.pending);
887
888         if (ctx->elapsed_updater)
889                 ecore_timer_del(ctx->elapsed_updater);
890
891         eina_stringshare_del(ctx->disconnected.number);
892
893         eina_list_free(ctx->multiparty.calls);
894
895         eina_list_free(ctx->calls.list);
896         free(ctx);
897 }
898
899 Evas_Object *callscreen_add(Evas_Object *parent)
900 {
901         Callscreen *ctx;
902         Evas_Object *obj = gui_layout_add(parent, "call");
903         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
904
905         ctx = calloc(1, sizeof(Callscreen));
906         ctx->self = obj;
907         ctx->tones.todo = eina_strbuf_new();
908
909         evas_object_data_set(obj, "callscreen.ctx", ctx);
910
911         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
912                                         _on_del, ctx);
913         elm_object_signal_callback_add(obj, "pressed,*", "call",
914                                         _on_pressed, ctx);
915         elm_object_signal_callback_add(obj, "released,*", "call",
916                                         _on_released, ctx);
917         elm_object_signal_callback_add(obj, "clicked,*", "call",
918                                         _on_clicked, ctx);
919
920         elm_object_focus_allow_set(obj, EINA_TRUE);
921         evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN,
922                                         _on_key_down, ctx);
923
924         elm_object_part_text_set(obj, "elm.text.name", "");
925         elm_object_part_text_set(obj, "elm.text.status", "");
926         elm_object_part_text_set(obj, "elm.text.elapsed", "");
927
928         ctx->multiparty.sc = elm_scroller_add(obj);
929         elm_scroller_policy_set(ctx->multiparty.sc, ELM_SCROLLER_POLICY_AUTO,
930                                 ELM_SCROLLER_POLICY_OFF);
931         elm_scroller_bounce_set(ctx->multiparty.sc, EINA_FALSE, EINA_TRUE);
932         elm_object_style_set(ctx->multiparty.sc, "multiparty-details");
933
934         ctx->multiparty.bx = elm_box_add(obj);
935         evas_object_size_hint_weight_set(ctx->multiparty.bx,
936                                                 EVAS_HINT_EXPAND, 0.0);
937         evas_object_size_hint_align_set(ctx->multiparty.bx,
938                                         EVAS_HINT_FILL, 0.0);
939         evas_object_show(ctx->multiparty.bx);
940         elm_object_content_set(ctx->multiparty.sc, ctx->multiparty.bx);
941
942         elm_object_part_content_set(obj, "elm.swallow.multiparty-details",
943                                         ctx->multiparty.sc);
944
945         callback_node_call_added = ofono_call_added_cb_add(_call_added, ctx);
946
947         callback_node_call_removed =
948                 ofono_call_removed_cb_add(_call_removed, ctx);
949
950         callback_node_call_changed =
951                 ofono_call_changed_cb_add(_call_changed, ctx);
952
953         callback_node_call_disconnected =
954                 ofono_call_disconnected_cb_add(_call_disconnected, ctx);
955
956         callback_node_modem_changed =
957                 ofono_modem_changed_cb_add(_ofono_changed, ctx);
958
959         return obj;
960 }