4 #include <Elementary.h>
11 #include "simple-popup.h"
13 /* timeout before a popup is show for supplementary services. It is
14 * not shown immediately as the call may fail as "not supported" in
15 * that case the dial will be done and we don't want a popup before
18 #define SS_POPUP_SHOW_TIMEOUT (0.5)
20 /* timeout to change keys into modified, like 0 -> + */
21 #define MOD_TIMEOUT (1.0)
23 /* timeouts to repeat key while pressed */
24 #define REP_TIMEOUT_INIT (0.3)
25 #define REP_TIMEOUT (0.2)
27 typedef struct _Keypad
30 Evas_Object *ss_popup;
31 Ecore_Timer *ss_popup_show_timeout;
32 OFono_Pending *ss_pending;
35 Ecore_Timer *mod_timeout;
36 Ecore_Timer *rep_timeout;
39 static void _number_display(Keypad *ctx)
41 const char *number = eina_strbuf_string_get(ctx->number);
42 char *s = phone_format(number);
45 elm_object_part_text_set(ctx->self, "elm.text.display", "");
46 elm_object_part_text_set(ctx->self, "elm.text.contact-and-type",
48 elm_object_part_text_set(ctx->self, "elm.text.contact", "");
49 elm_object_part_text_set(ctx->self, "elm.text.phone.type", "");
50 elm_object_signal_emit(ctx->self, "disable,save", "keypad");
51 elm_object_signal_emit(ctx->self, "disable,backspace",
53 elm_object_signal_emit(ctx->self, "hide,contact", "keypad");
57 Contact_Info *info = gui_contact_search(number, &type);
59 const char *name = contact_info_full_name_get(info);
62 snprintf(buf, sizeof(buf), "%s - %s", name, type);
64 elm_object_part_text_set(ctx->self, "elm.text.contact-and-type",
66 elm_object_part_text_set(ctx->self, "elm.text.contact", name);
67 elm_object_part_text_set(ctx->self, "elm.text.phone.type", type);
68 elm_object_signal_emit(ctx->self, "show,contact", "keypad");
70 elm_object_signal_emit(ctx->self, "hide,contact", "keypad");
71 elm_object_part_text_set(ctx->self, "elm.text.contact-and-type",
73 elm_object_part_text_set(ctx->self, "elm.text.contact", "");
74 elm_object_part_text_set(ctx->self, "elm.text.phone.type", "");
77 elm_object_part_text_set(ctx->self, "elm.text.display", s);
80 elm_object_signal_emit(ctx->self, "enable,save", "keypad");
81 elm_object_signal_emit(ctx->self, "enable,backspace", "keypad");
85 static void _imei_show(void)
87 const char *imei = ofono_modem_serial_get();
88 gui_simple_popup("IMEI Request", imei ? imei : "No modem");
89 INF("Show IMEI: %s", imei);
92 static void _pin_reply(void *data, OFono_Error error)
94 const char *title = data;
95 const char *msg = ofono_error_message_get(error);
97 gui_simple_popup(title, msg);
98 if (error == OFONO_ERROR_NONE)
104 static void _change_pin(const char *what, const char *old, const char *new)
106 DBG("ask change of %s from %s to %s", what, old, new);
107 ofono_modem_change_pin(what, old, new, _pin_reply, "PIN Change");
110 static void _reset_pin(const char *what, const char *old, const char *new)
112 DBG("ask reset of %s using %s to %s", what, old, new);
113 ofono_modem_reset_pin(what, old, new, _pin_reply, "PIN Reset");
116 static void _parse_pin(const char *str, char **old, char **new)
118 const char *p1, *p2, *p3;
119 int len1, len2, len3;
121 p1 = strchr(str, '*');
125 p2 = strchr(p1 + 1, '*');
129 p3 = strchr(p2 + 1, '#');
134 len2 = p2 - (p1 + 1);
135 len3 = p3 - (p2 + 1);
137 if ((len2 != len3) || (memcmp(p1 + 1, p2 + 1, len2) != 0)) {
138 ERR("New PIN code check failed: '%.*s' '%.*s'",
144 *old = strndup(str, len1);
145 *new = strndup(p1 + 1, len2);
150 ERR("Invalid PIN change format: %s", str);
153 static Eina_Bool _handle_if_mmi(Keypad *ctx)
155 const char *str = eina_strbuf_string_get(ctx->number);
156 size_t len = eina_strbuf_length_get(ctx->number);
161 if (len < sizeof("*#06#") - 1)
166 if (str[len - 1] != '#')
169 DBG("Possible MMI code: %s", str);
174 if (strcmp(str, "#06#") == 0) {
176 eina_strbuf_reset(ctx->number);
177 _number_display(ctx);
179 } else if (strncmp(str, "*0", 2) == 0) {
180 const char *what = NULL, *p = str + 2;
181 char *old = NULL, *new = NULL;
182 void (*action)(const char *, const char *, const char *) = NULL;
185 /* MMI codes to change PIN:
186 * - **04*OLD_PIN*NEW_PIN*NEW_PIN#
187 * - **042*OLD-PIN2*NEW_PIN2*NEW_PIN2#
190 action = _change_pin;
194 } else if (strncmp(p, "2*", 2) == 0) {
200 _parse_pin(p, &old, &new);
201 } else if (*p == '5') {
202 /* MMI codes to reset PIN:
203 * - **05*PIN_UNBLOCKING_KEY*NEW_PIN*NEW_PIN#
204 * - **052*PIN2_UNBLOCKING_KEY*NEW_PIN2*NEW_PIN2#
211 } else if (strncmp(p, "2*", 2) == 0) {
217 _parse_pin(p, &old, &new);
220 DBG("PIN management '%s' what=%s, old=%s, new=%s",
221 str, what, old, new);
222 if (action && what && old && new) {
223 action(what, old, new);
224 eina_strbuf_reset(ctx->number);
225 _number_display(ctx);
236 static void _dial(Keypad *ctx)
238 const char *number = eina_strbuf_string_get(ctx->number);
240 INF("call %s", number);
242 eina_stringshare_replace(&(ctx->last), number);
243 eina_strbuf_reset(ctx->number);
244 _number_display(ctx);
247 static void _ss_initiate_reply(void *data, OFono_Error err, const char *str)
251 DBG("e=%d, str=%s, ss_popup_show_timeout=%p, ss_popup=%p",
252 err, str, ctx->ss_popup_show_timeout, ctx->ss_popup);
254 ctx->ss_pending = NULL;
256 if (ctx->ss_popup_show_timeout) {
257 ecore_timer_del(ctx->ss_popup_show_timeout);
258 ctx->ss_popup_show_timeout = NULL;
261 if ((err == OFONO_ERROR_NOT_RECOGNIZED) ||
262 (err == OFONO_ERROR_OFFLINE) ||
263 (err == OFONO_ERROR_INVALID_FORMAT)) {
265 evas_object_del(ctx->ss_popup);
266 } else if (err != OFONO_ERROR_NONE) {
269 /* no popup? then it was canceled */
273 snprintf(buf, sizeof(buf), "Could not complete.<br>Error: %s",
274 ofono_error_message_get(err));
275 simple_popup_title_set(ctx->ss_popup, "Error");
276 simple_popup_message_set(ctx->ss_popup, buf);
277 simple_popup_button_dismiss_set(ctx->ss_popup);
278 evas_object_show(ctx->ss_popup);
280 evas_object_del(ctx->ss_popup);
281 eina_strbuf_reset(ctx->number);
282 _number_display(ctx);
288 static Eina_Bool _on_ss_popup_show_timeout(void *data)
293 evas_object_show(ctx->ss_popup);
295 ctx->ss_popup_show_timeout = NULL;
299 static void _on_ss_popup_del(void *data, Evas *e __UNUSED__,
300 Evas_Object *o __UNUSED__,
301 void *event_info __UNUSED__)
305 ctx->ss_popup = NULL;
307 if (ctx->ss_popup_show_timeout) {
308 ecore_timer_del(ctx->ss_popup_show_timeout);
309 ctx->ss_popup_show_timeout = NULL;
312 if (ctx->ss_pending) {
313 ofono_pending_cancel(ctx->ss_pending);
314 ctx->ss_pending = NULL;
318 /* Procedure as ofono/doc/mmi-codes.txt:
319 * - send number to SupplementaryServices.Initiate()
320 * - if NotRecognized is returned, then forward VoiceCallManager.Dial()
322 static void _call(Keypad *ctx)
324 const char *number = eina_strbuf_string_get(ctx->number);
325 int len = eina_strbuf_length_get(ctx->number);
327 INF("calling %s...", number);
333 evas_object_del(ctx->ss_popup);
336 ofono_pending_cancel(ctx->ss_pending);
338 ctx->ss_popup = gui_simple_popup(NULL, NULL);
339 evas_object_event_callback_add(ctx->ss_popup, EVAS_CALLBACK_DEL,
340 _on_ss_popup_del, ctx);
341 evas_object_hide(ctx->ss_popup);
342 if (ctx->ss_popup_show_timeout)
343 ecore_timer_del(ctx->ss_popup_show_timeout);
344 ctx->ss_popup_show_timeout = ecore_timer_add(
345 SS_POPUP_SHOW_TIMEOUT, _on_ss_popup_show_timeout, ctx);
347 ctx->ss_pending = ofono_ss_initiate(number, _ss_initiate_reply, ctx);
350 static Eina_Bool _on_mod_timeout(void *data)
353 size_t len = eina_strbuf_length_get(ctx->number);
356 const char *str = eina_strbuf_string_get(ctx->number);
357 if (str[len - 1] == '0') {
358 eina_strbuf_remove(ctx->number, len - 1, len);
359 eina_strbuf_append_char(ctx->number, '+');
360 _number_display(ctx);
364 ctx->mod_timeout = NULL;
368 static Eina_Bool _on_rep_timeout(void *data)
371 size_t len = eina_strbuf_length_get(ctx->number);
374 eina_strbuf_remove(ctx->number, len - 1, len);
375 _number_display(ctx);
379 ctx->rep_timeout = NULL;
383 ecore_timer_interval_set(ctx->rep_timeout, REP_TIMEOUT);
387 static void _on_pressed(void *data, Evas_Object *obj __UNUSED__,
388 const char *emission, const char *source __UNUSED__)
391 DBG("ctx=%p, signal: %s", ctx, emission);
393 EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "pressed,"));
394 emission += strlen("pressed,");
396 if ((emission[0] >= '0') && (emission[0] <= '9')) {
397 eina_strbuf_append_char(ctx->number, emission[0]);
398 _number_display(ctx);
400 if ((emission[0] == '0') &&
401 (eina_strbuf_length_get(ctx->number) == 1)) {
402 if (ctx->mod_timeout)
403 ecore_timer_del(ctx->mod_timeout);
404 ctx->mod_timeout = ecore_timer_add(MOD_TIMEOUT,
405 _on_mod_timeout, ctx);
408 } else if (strcmp(emission, "backspace") == 0) {
409 size_t len = eina_strbuf_length_get(ctx->number);
411 eina_strbuf_remove(ctx->number, len - 1, len);
412 _number_display(ctx);
414 if (ctx->rep_timeout)
415 ecore_timer_del(ctx->rep_timeout);
416 ctx->rep_timeout = ecore_timer_add(
417 REP_TIMEOUT_INIT, _on_rep_timeout, ctx);
421 } else if (strcmp(emission, "star") == 0) {
422 eina_strbuf_append_char(ctx->number, '*');
423 _number_display(ctx);
424 } else if (strcmp(emission, "hash") == 0) {
425 eina_strbuf_append_char(ctx->number, '#');
426 _number_display(ctx);
432 static void _on_released(void *data, Evas_Object *obj __UNUSED__,
433 const char *emission,
434 const char *source __UNUSED__)
437 DBG("ctx=%p, signal: %s", ctx, emission);
439 EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "released,"));
440 emission += strlen("released,");
442 if (ctx->mod_timeout) {
443 if (emission[0] != '0')
444 ERR("Expected '0' but got '%s' instead", emission);
445 ecore_timer_del(ctx->mod_timeout);
446 ctx->mod_timeout = NULL;
449 if (ctx->rep_timeout) {
450 if (strcmp(emission, "backspace") != 0)
451 ERR("Expected 'backspace' but got '%s' instead",
453 ecore_timer_del(ctx->rep_timeout);
454 ctx->rep_timeout = NULL;
458 static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
459 const char *emission, const char *source __UNUSED__)
462 DBG("ctx=%p, signal: %s", ctx, emission);
464 EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "clicked,"));
465 emission += strlen("clicked,");
467 if (strcmp(emission, "call") == 0) {
468 if (eina_strbuf_length_get(ctx->number) > 0)
470 else if (ctx->last) {
471 eina_strbuf_append(ctx->number, ctx->last);
472 _number_display(ctx);
474 } else if (strcmp(emission, "save") == 0) {
475 ERR("TODO save contact %s!",
476 eina_strbuf_string_get(ctx->number));
480 static void _on_del(void *data, Evas *e __UNUSED__,
481 Evas_Object *obj __UNUSED__, void *event __UNUSED__)
486 ofono_pending_cancel(ctx->ss_pending);
488 if (ctx->mod_timeout)
489 ecore_timer_del(ctx->mod_timeout);
490 if (ctx->rep_timeout)
491 ecore_timer_del(ctx->rep_timeout);
492 if (ctx->ss_popup_show_timeout)
493 ecore_timer_del(ctx->ss_popup_show_timeout);
495 eina_strbuf_free(ctx->number);
496 eina_stringshare_del(ctx->last);
500 Evas_Object *keypad_add(Evas_Object *parent)
503 Evas_Object *obj = layout_add(parent, "keypad");
504 EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
506 ctx = calloc(1, sizeof(Keypad));
508 ctx->number = eina_strbuf_new();
510 evas_object_data_set(obj, "keypad.ctx", ctx);
512 evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
514 elm_object_signal_callback_add(obj, "pressed,*", "keypad",
516 elm_object_signal_callback_add(obj, "released,*", "keypad",
518 elm_object_signal_callback_add(obj, "clicked,*", "keypad",
521 elm_object_part_text_set(obj, "elm.text.display", "");
522 elm_object_part_text_set(ctx->self, "elm.text.contact-and-type", "");
523 elm_object_part_text_set(ctx->self, "elm.text.contact", "");
524 elm_object_part_text_set(ctx->self, "elm.text.phone.type", "");
525 elm_object_signal_emit(obj, "hide,contact", "keypad");
526 elm_object_signal_emit(obj, "disable,save", "keypad");
527 elm_object_signal_emit(obj, "disable,backspace", "keypad");
529 elm_object_focus_allow_set(obj, EINA_TRUE);
534 void keypad_number_set(Evas_Object *obj, const char *number,
538 EINA_SAFETY_ON_NULL_RETURN(obj);
539 EINA_SAFETY_ON_NULL_RETURN(number);
541 ctx = evas_object_data_get(obj, "keypad.ctx");
542 EINA_SAFETY_ON_NULL_RETURN(ctx);
544 eina_strbuf_reset(ctx->number);
545 eina_strbuf_append(ctx->number, number);
546 _number_display(ctx);