move gui_layout_add() from gui.h to layout_add() in utils.h
[profile/ivi/lemolo.git] / dialer / gui.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 "keypad.h"
9 #include "history.h"
10 #include "callscreen.h"
11 #include "ussd.h"
12 #include "util.h"
13
14 #ifdef HAVE_TIZEN
15 #include <appcore-efl.h>
16 #include <ui-gadget.h>
17 #include <Ecore_X.h>
18 #endif
19
20 static Evas_Object *win = NULL;
21 static Evas_Object *main_layout = NULL;
22 static Evas_Object *keypad = NULL;
23 static Evas_Object *contacts = NULL;
24 static Evas_Object *history = NULL;
25 static Evas_Object *cs = NULL;
26 static Evas_Object *flip = NULL;
27
28 static OFono_Callback_List_Modem_Node *callback_node_modem_changed = NULL;
29 static OFono_Callback_List_USSD_Notify_Node *callback_node_ussd_notify = NULL;
30
31 /* XXX elm_flip should just do the right thing, but it does not */
32 static Eina_Bool in_call = EINA_FALSE;
33 static Eina_Bool in_flip_anim = EINA_FALSE;
34
35 Contact_Info *gui_contact_search(const char *number, const char **type)
36 {
37         return contact_search(contacts, number, type);
38 }
39
40 static void _dial_reply(void *data, OFono_Error err,
41                         OFono_Call *call __UNUSED__)
42 {
43         char *number = data;
44
45         if (err != OFONO_ERROR_NONE) {
46                 char buf[1024];
47                 const char *msg = ofono_error_message_get(err);
48                 snprintf(buf, sizeof(buf), "Could not call %s: %s",
49                                 number, msg);
50                 gui_simple_popup("Error", buf);
51         }
52
53         free(number);
54 }
55
56 OFono_Pending *gui_dial(const char *number)
57 {
58         char *copy = strdup(number);
59         return ofono_dial(copy, NULL, _dial_reply, copy);
60 }
61
62 static void _gui_show(Evas_Object *o)
63 {
64         if (o == keypad)
65                 elm_object_signal_emit(main_layout, "show,keypad", "gui");
66         else if (o == contacts)
67                 elm_object_signal_emit(main_layout, "show,contacts", "gui");
68         else if (o == history)
69                 elm_object_signal_emit(main_layout, "show,history", "gui");
70         elm_object_focus_set(o, EINA_TRUE);
71 }
72
73 typedef struct _Simple_Popup
74 {
75         Evas_Object *popup;
76         Evas_Object *box;
77         Evas_Object *message;
78         Evas_Object *entry;
79         Ecore_Timer *recalc_timer; /* HACK */
80 } Simple_Popup;
81
82 static void _popup_close(void *data, Evas_Object *bt __UNUSED__, void *event __UNUSED__)
83 {
84         Evas_Object *popup = data;
85         evas_object_del(popup);
86 }
87
88 void gui_simple_popup_title_set(Evas_Object *p, const char *title)
89 {
90         if (title) {
91                 elm_object_part_text_set(p, "elm.text.title", title);
92                 elm_object_signal_emit(p, "show,title", "gui");
93         } else {
94                 elm_object_part_text_set(p, "elm.text.title", "");
95                 elm_object_signal_emit(p, "hide,title", "gui");
96         }
97 }
98
99 /* HACK: force recalc from an idler to fix elm_entry problem */
100 static Eina_Bool _gui_simple_popup_entries_reeval(void *data)
101 {
102         Simple_Popup *ctx = data;
103         if (ctx->message)
104                 elm_entry_calc_force(ctx->message);
105         if (ctx->entry)
106                 elm_entry_calc_force(ctx->entry);
107         ctx->recalc_timer = NULL;
108         return EINA_FALSE;
109 }
110
111 static void _gui_simple_popup_timer_cancel_if_needed(Simple_Popup *ctx)
112 {
113         if (!ctx->recalc_timer)
114                 return;
115         if (ctx->message || ctx->entry)
116                 return;
117         ecore_timer_del(ctx->recalc_timer);
118         ctx->recalc_timer = NULL;
119 }
120
121 static void _gui_simple_popup_message_del(void *data, Evas *e __UNUSED__,
122                                                 Evas_Object *en __UNUSED__,
123                                                 void *einfo __UNUSED__)
124 {
125         Simple_Popup *ctx = data;
126         ctx->message = NULL;
127         _gui_simple_popup_timer_cancel_if_needed(ctx);
128 }
129
130 static void _gui_simple_popup_entry_del(void *data, Evas *e __UNUSED__,
131                                         Evas_Object *en __UNUSED__,
132                                         void *einfo __UNUSED__)
133 {
134         Simple_Popup *ctx = data;
135         ctx->entry = NULL;
136         _gui_simple_popup_timer_cancel_if_needed(ctx);
137 }
138
139 static void _gui_simple_popup_reeval_content(Simple_Popup *ctx)
140 {
141         if ((!ctx->message) && (!ctx->entry)) {
142                 elm_object_part_content_unset(ctx->popup,
143                                                 "elm.swallow.content");
144                 elm_object_signal_emit(ctx->popup, "hide,content", "gui");
145                 evas_object_hide(ctx->box);
146                 return;
147         }
148
149         elm_box_unpack_all(ctx->box);
150         if (ctx->message) {
151                 elm_box_pack_end(ctx->box, ctx->message);
152                 evas_object_show(ctx->message);
153         }
154         if (ctx->entry) {
155                 elm_box_pack_end(ctx->box, ctx->entry);
156                 evas_object_show(ctx->entry);
157         }
158
159         elm_object_part_content_set(ctx->popup, "elm.swallow.content",
160                                         ctx->box);
161         elm_object_signal_emit(ctx->popup, "show,content", "gui");
162
163         /* HACK: elm_entry is not evaluating properly and the text is
164          * not centered as it should be. Then we must force a
165          * calculation from an timer.
166          */
167         if (ctx->recalc_timer)
168                 ecore_timer_del(ctx->recalc_timer);
169         ctx->recalc_timer = ecore_timer_add(
170                 0.02, _gui_simple_popup_entries_reeval, ctx);
171 }
172
173 void gui_simple_popup_message_set(Evas_Object *p, const char *msg)
174 {
175         Simple_Popup *ctx = evas_object_data_get(p, "simple_popup");
176         Evas_Object *en;
177
178         EINA_SAFETY_ON_NULL_RETURN(ctx);
179
180         if (!msg) {
181                 if (ctx->message)
182                         evas_object_del(ctx->message);
183                 _gui_simple_popup_reeval_content(ctx);
184                 return;
185         }
186
187         if (ctx->message)
188                 en = ctx->message;
189         else {
190                 en = ctx->message = elm_entry_add(p);
191                 elm_object_style_set(en, "dialer-popup");
192                 elm_entry_editable_set(en, EINA_FALSE);
193                 elm_entry_scrollable_set(en, EINA_TRUE);
194 #if ELM_VERSION_MAJOR == 1 && ELM_VERSION_MINOR < 7
195                 /* old, deprecated api */
196                 elm_entry_scrollbar_policy_set(en, ELM_SCROLLER_POLICY_OFF,
197                                                 ELM_SCROLLER_POLICY_AUTO);
198 #else
199                 elm_scroller_policy_set(en, ELM_SCROLLER_POLICY_OFF,
200                                         ELM_SCROLLER_POLICY_AUTO);
201 #endif
202
203                 evas_object_event_callback_add(en, EVAS_CALLBACK_DEL,
204                                         _gui_simple_popup_message_del,
205                                         ctx);
206                 evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND,
207                                                         EVAS_HINT_EXPAND);
208                 evas_object_size_hint_align_set(en, EVAS_HINT_FILL,
209                                                 EVAS_HINT_FILL);
210         }
211
212         elm_object_text_set(en, msg);
213         _gui_simple_popup_reeval_content(ctx);
214 }
215
216 void gui_simple_popup_entry_enable(Evas_Object *p)
217 {
218         Simple_Popup *ctx = evas_object_data_get(p, "simple_popup");
219         Evas_Object *en;
220
221         EINA_SAFETY_ON_NULL_RETURN(ctx);
222
223         if (ctx->entry)
224                 return;
225         en = ctx->entry = elm_entry_add(p);
226         elm_object_style_set(en, "dialer-popup-editable");
227         elm_entry_editable_set(en, EINA_TRUE);
228         elm_entry_scrollable_set(en, EINA_TRUE);
229 #if ELM_VERSION_MAJOR == 1 && ELM_VERSION_MINOR < 7
230         /* old, deprecated api */
231         elm_entry_scrollbar_policy_set(en, ELM_SCROLLER_POLICY_OFF,
232                                         ELM_SCROLLER_POLICY_AUTO);
233 #else
234         elm_scroller_policy_set(en, ELM_SCROLLER_POLICY_OFF,
235                                 ELM_SCROLLER_POLICY_AUTO);
236 #endif
237
238         evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND,
239                                                 EVAS_HINT_EXPAND);
240         evas_object_size_hint_align_set(en, EVAS_HINT_FILL,
241                                         EVAS_HINT_FILL);
242
243         evas_object_event_callback_add(en, EVAS_CALLBACK_DEL,
244                                         _gui_simple_popup_entry_del,
245                                         ctx);
246         _gui_simple_popup_reeval_content(ctx);
247 }
248
249 void gui_simple_popup_entry_disable(Evas_Object *p)
250 {
251         Simple_Popup *ctx = evas_object_data_get(p, "simple_popup");
252         EINA_SAFETY_ON_NULL_RETURN(ctx);
253         if (!ctx->entry)
254                 return;
255         evas_object_del(ctx->entry);
256         _gui_simple_popup_reeval_content(ctx);
257 }
258
259 const char *gui_simple_popup_entry_get(const Evas_Object *p)
260 {
261         Simple_Popup *ctx = evas_object_data_get(p, "simple_popup");
262         EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
263         EINA_SAFETY_ON_NULL_RETURN_VAL(ctx->entry, NULL);
264         return elm_object_text_get(ctx->entry);
265 }
266
267 void gui_simple_popup_button_dismiss_set(Evas_Object *p)
268 {
269         gui_simple_popup_buttons_set(p,
270                                         "Dismiss",
271                                         "dialer",
272                                         _popup_close,
273                                         NULL, NULL, NULL,
274                                         p);
275 }
276
277 void gui_simple_popup_buttons_set(Evas_Object *p,
278                                         const char *b1_label,
279                                         const char *b1_class,
280                                         Evas_Smart_Cb b1_cb,
281                                         const char *b2_label,
282                                         const char *b2_class,
283                                         Evas_Smart_Cb b2_cb,
284                                         const void *data)
285 {
286         Evas_Object *bt;
287         unsigned int count = 0;
288
289         if (b1_label) {
290                 bt = elm_button_add(p);
291                 elm_object_style_set(bt, b1_class ? b1_class : "dialer");
292                 elm_object_text_set(bt, b1_label);
293                 elm_object_part_content_set(p, "elm.swallow.button1", bt);
294                 evas_object_smart_callback_add(bt, "clicked", b1_cb, data);
295                 count++;
296         }
297
298         if (b2_label) {
299                 const char *part;
300                 bt = elm_button_add(p);
301                 elm_object_style_set(bt, b2_class ? b2_class : "dialer");
302                 elm_object_text_set(bt, b2_label);
303
304                 if (count == 1)
305                         part = "elm.swallow.button2";
306                 else
307                         part = "elm.swallow.button1";
308
309                 elm_object_part_content_set(p, part, bt);
310                 evas_object_smart_callback_add(bt, "clicked", b2_cb, data);
311                 count++;
312         }
313
314         if (count == 2)
315                 elm_object_signal_emit(p, "buttons,2", "gui");
316         else if (count == 1) {
317                 elm_object_signal_emit(p, "buttons,1", "gui");
318                 elm_object_part_content_set(p, "elm.swallow.button2", NULL);
319         } else {
320                 elm_object_signal_emit(p, "buttons,0", "gui");
321                 elm_object_part_content_set(p, "elm.swallow.button1", NULL);
322                 elm_object_part_content_set(p, "elm.swallow.button2", NULL);
323         }
324 }
325
326 static void _gui_simple_popup_del(void *data, Evas *e __UNUSED__,
327                                         Evas_Object *o __UNUSED__,
328                                         void *event_info __UNUSED__)
329 {
330         Simple_Popup *ctx = data;
331         free(ctx);
332 }
333
334 Evas_Object *gui_simple_popup(const char *title, const char *message)
335 {
336         Evas_Object *p = layout_add(win, "popup");
337         Simple_Popup *ctx;
338
339         EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
340
341         ctx = calloc(1, sizeof(Simple_Popup));
342         EINA_SAFETY_ON_NULL_GOTO(ctx, failed_calloc);
343
344         evas_object_data_set(p, "simple_popup", ctx);
345         ctx->popup = p;
346         evas_object_event_callback_add(p, EVAS_CALLBACK_DEL,
347                                         _gui_simple_popup_del, ctx);
348
349         evas_object_size_hint_weight_set(p, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
350         elm_win_resize_object_add(win, p);
351
352         ctx->box = elm_box_add(p);
353
354         gui_simple_popup_title_set(p, title);
355         gui_simple_popup_message_set(p, message);
356         gui_simple_popup_button_dismiss_set(p);
357
358         evas_object_show(p);
359
360         return p;
361
362 failed_calloc:
363         evas_object_del(p);
364         return NULL;
365 }
366
367 void gui_activate(void)
368 {
369         elm_win_raise(win);
370         elm_win_activate(win);
371         evas_object_show(win);
372 }
373
374 void gui_number_set(const char *number, Eina_Bool auto_dial)
375 {
376         _gui_show(keypad);
377         keypad_number_set(keypad, number, auto_dial);
378 }
379
380 void gui_activecall_set(Evas_Object *o)
381 {
382         if (o) {
383                 elm_object_part_content_set(main_layout,
384                                                 "elm.swallow.activecall", o);
385                 elm_object_signal_emit(main_layout, "show,activecall", "gui");
386         } else {
387                 o = elm_object_part_content_unset(
388                         main_layout, "elm.swallow.activecall");
389                 elm_object_signal_emit(main_layout, "hide,activecall", "gui");
390                 evas_object_hide(o);
391         }
392 }
393
394 void gui_contacts_show(void)
395 {
396         _gui_show(contacts);
397         gui_call_exit();
398 }
399
400 void gui_call_enter(void)
401 {
402         gui_activate();
403         if (in_call)
404                 return;
405         in_call = EINA_TRUE;
406         if (in_flip_anim)
407                 return;
408         in_flip_anim = EINA_TRUE;
409         elm_flip_go(flip, ELM_FLIP_ROTATE_Y_CENTER_AXIS);
410         elm_object_focus_set(cs, EINA_TRUE);
411 }
412
413 void gui_call_exit(void)
414 {
415         if (!in_call)
416                 return;
417         in_call = EINA_FALSE;
418         if (in_flip_anim)
419                 return;
420         in_flip_anim = EINA_TRUE;
421         elm_flip_go(flip, ELM_FLIP_ROTATE_Y_CENTER_AXIS);
422         elm_object_focus_set(cs, EINA_FALSE);
423 }
424
425 static void _gui_call_sync(void *data __UNUSED__, Evas_Object *o __UNUSED__,
426                                 void *event_info __UNUSED__)
427 {
428         Eina_Bool showing_call = !elm_flip_front_visible_get(flip);
429
430         if (showing_call ^ in_call) {
431                 DBG("Flip back to sync");
432                 elm_flip_go(flip, ELM_FLIP_ROTATE_Y_CENTER_AXIS);
433                 elm_object_focus_set(cs, in_call);
434         }
435         in_flip_anim = EINA_FALSE;
436 }
437
438 static void _gui_voicemail(void)
439 {
440         const char *number = ofono_voicemail_number_get();
441         EINA_SAFETY_ON_NULL_RETURN(number);
442         EINA_SAFETY_ON_FALSE_RETURN(*number != '\0');
443
444         gui_dial(number);
445 }
446
447 static void _on_clicked(void *data __UNUSED__, Evas_Object *o __UNUSED__,
448                         const char *emission, const char *source __UNUSED__)
449 {
450         DBG("signal: %s", emission);
451
452         EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "clicked,"));
453         emission += strlen("clicked,");
454
455         if (strcmp(emission, "keypad") == 0)
456                 _gui_show(keypad);
457         else if (strcmp(emission, "contacts") == 0)
458                 _gui_show(contacts);
459         else if (strcmp(emission, "history") == 0)
460                 _gui_show(history);
461         else if (strcmp(emission, "voicemail") == 0)
462                 _gui_voicemail();
463 }
464
465 static void _ofono_changed(void *data __UNUSED__)
466 {
467         const char *number;
468         Eina_Bool waiting;
469         unsigned char count;
470         char buf[32];
471
472         if ((ofono_modem_api_get() & OFONO_API_MSG_WAITING) == 0) {
473                 elm_object_signal_emit(main_layout, "disable,voicemail", "gui");
474                 elm_object_signal_emit(main_layout,
475                                         "toggle,off,voicemail", "gui");
476                 elm_object_part_text_set(main_layout, "elm.text.voicemail", "");
477                 return;
478         }
479
480         number = ofono_voicemail_number_get();
481         waiting = ofono_voicemail_waiting_get();
482         count = ofono_voicemail_count_get();
483
484         if (number)
485                 elm_object_signal_emit(main_layout, "enable,voicemail", "gui");
486         else
487                 elm_object_signal_emit(main_layout, "disable,voicemail", "gui");
488
489         if (waiting) {
490                 if (count == 0)
491                         snprintf(buf, sizeof(buf), "*");
492                 else if (count < 255)
493                         snprintf(buf, sizeof(buf), "%d", count);
494                 else
495                         snprintf(buf, sizeof(buf), "255+");
496
497                 elm_object_signal_emit(main_layout,
498                                         "toggle,on,voicemail", "gui");
499         } else {
500                 buf[0] = '\0';
501                 elm_object_signal_emit(main_layout,
502                                         "toggle,off,voicemail", "gui");
503         }
504
505         elm_object_part_text_set(main_layout, "elm.text.voicemail", buf);
506 }
507
508 static void _ofono_ussd_notify(void *data __UNUSED__, Eina_Bool needs_reply,
509                                 const char *message)
510 {
511         DBG("needs_reply=%d, message=%s", needs_reply, message);
512         ussd_start(message);
513 }
514
515 Eina_Bool gui_init(void)
516 {
517         Evas_Object *lay, *obj;
518         Evas_Coord w, h;
519
520         /* dialer should never, ever quit */
521         elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_NONE);
522
523         win = elm_win_util_standard_add("ofono-dialer", "oFono Dialer");
524         EINA_SAFETY_ON_NULL_RETURN_VAL(win, EINA_FALSE);
525         elm_win_autodel_set(win, EINA_FALSE);
526
527 #ifdef HAVE_TIZEN
528         appcore_set_i18n("ofono-efl", "en-US");
529         UG_INIT_EFL(win, UG_OPT_INDICATOR_PORTRAIT_ONLY);
530 #endif
531
532         flip = elm_flip_add(win);
533         evas_object_size_hint_weight_set(flip,
534                                 EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
535         evas_object_size_hint_align_set(flip, EVAS_HINT_FILL, EVAS_HINT_FILL);
536         elm_win_resize_object_add(win, flip);
537         evas_object_smart_callback_add(flip, "animate,done",
538                                         _gui_call_sync, NULL);
539         evas_object_show(flip);
540
541         main_layout = lay = layout_add(win, "main");
542         EINA_SAFETY_ON_NULL_RETURN_VAL(lay, EINA_FALSE);
543         evas_object_size_hint_weight_set(lay,
544                                 EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
545         evas_object_size_hint_align_set(lay, EVAS_HINT_FILL, EVAS_HINT_FILL);
546         elm_object_part_content_set(flip, "front", lay);
547         evas_object_show(lay);
548
549         elm_object_signal_callback_add(lay, "clicked,*", "gui",
550                                         _on_clicked, NULL);
551
552         keypad = obj = keypad_add(win);
553         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
554         elm_object_part_content_set(lay, "elm.swallow.keypad", obj);
555
556         contacts = obj = contacts_add(win);
557         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
558         elm_object_part_content_set(lay, "elm.swallow.contacts", obj);
559
560         history = obj = history_add(win);
561         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
562         elm_object_part_content_set(lay, "elm.swallow.history", obj);
563
564         _gui_show(keypad);
565
566         cs = obj = callscreen_add(win);
567         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
568         evas_object_size_hint_weight_set(obj,
569                                 EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
570         evas_object_size_hint_align_set(obj, EVAS_HINT_FILL, EVAS_HINT_FILL);
571         elm_object_part_content_set(flip, "back", obj);
572         evas_object_show(obj);
573
574         callback_node_modem_changed =
575                 ofono_modem_changed_cb_add(_ofono_changed, NULL);
576         callback_node_ussd_notify =
577                 ofono_ussd_notify_cb_add(_ofono_ussd_notify, NULL);
578
579         /* TODO: make it match better with Tizen: icon and other properties */
580         obj = elm_layout_edje_get(lay);
581         edje_object_size_min_get(obj, &w, &h);
582         if ((w == 0) || (h == 0))
583                 edje_object_size_min_restricted_calc(obj, &w, &h, w, h);
584         if ((w == 0) || (h == 0))
585                 edje_object_parts_extends_calc(obj, NULL, NULL, &w, &h);
586         evas_object_resize(win, w, h);
587
588         /* do not show it yet, RC will check if it should be visible or not */
589         return EINA_TRUE;
590 }
591
592 void gui_shutdown(void)
593 {
594 }