fix warning: unused parameter ‘reason’
[profile/ivi/lemolo.git] / dialer / keypad.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 /* timeout to change keys into modified, like 0 -> + */
12 #define MOD_TIMEOUT (1.0)
13
14 /* timeouts to repeat key while pressed */
15 #define REP_TIMEOUT_INIT (0.3)
16 #define REP_TIMEOUT (0.2)
17
18 typedef struct _Keypad
19 {
20         Evas_Object *self;
21         Eina_Strbuf *number;
22         const char *last;
23         Ecore_Timer *mod_timeout;
24         Ecore_Timer *rep_timeout;
25 } Keypad;
26
27 static void _number_display(Keypad *ctx)
28 {
29         const char *number = eina_strbuf_string_get(ctx->number);
30         char *s = phone_format(number);
31         const char *type;
32         if (!s) {
33                 elm_object_part_text_set(ctx->self, "elm.text.display", "");
34                 elm_object_part_text_set(ctx->self, "elm.text.contact-and-type",
35                                                 "");
36                 elm_object_part_text_set(ctx->self, "elm.text.contact", "");
37                 elm_object_part_text_set(ctx->self, "elm.text.phone.type", "");
38                 elm_object_signal_emit(ctx->self, "disable,save", "keypad");
39                 elm_object_signal_emit(ctx->self, "disable,backspace",
40                                         "keypad");
41                 elm_object_signal_emit(ctx->self, "hide,contact", "keypad");
42                 return;
43         }
44
45         Contact_Info *info = gui_contact_search(number, &type);
46         if (info) {
47                 const char *name = contact_info_full_name_get(info);
48                 char buf[1024];
49
50                 snprintf(buf, sizeof(buf), "%s - %s", name, type);
51
52                 elm_object_part_text_set(ctx->self, "elm.text.contact-and-type",
53                                                 buf);
54                 elm_object_part_text_set(ctx->self, "elm.text.contact", name);
55                 elm_object_part_text_set(ctx->self, "elm.text.phone.type", type);
56                 elm_object_signal_emit(ctx->self, "show,contact", "keypad");
57         } else {
58                 elm_object_signal_emit(ctx->self, "hide,contact", "keypad");
59                 elm_object_part_text_set(ctx->self, "elm.text.contact-and-type",
60                                                 "");
61                 elm_object_part_text_set(ctx->self, "elm.text.contact", "");
62                 elm_object_part_text_set(ctx->self, "elm.text.phone.type", "");
63         }
64
65         elm_object_part_text_set(ctx->self, "elm.text.display", s);
66         free(s);
67
68         elm_object_signal_emit(ctx->self, "enable,save", "keypad");
69         elm_object_signal_emit(ctx->self, "enable,backspace", "keypad");
70 }
71
72
73 static void _imei_show(void)
74 {
75         const char *imei = ofono_modem_serial_get();
76         gui_simple_popup("IMEI Request", imei ? imei : "No modem");
77         INF("Show IMEI: %s", imei);
78 }
79
80 static void _pin_reply(void *data, OFono_Error error)
81 {
82         const char *title = data;
83         const char *msg;
84         switch (error) {
85         case OFONO_ERROR_NONE:
86                 msg = "Success";
87                 break;
88         case OFONO_ERROR_INVALID_ARGS:
89                 msg = "Invalid Arguments";
90                 break;
91         case OFONO_ERROR_INVALID_FORMAT:
92                 msg = "Invalid Format";
93                 break;
94         default:
95                 msg = "Failed";
96         }
97
98         gui_simple_popup(title, msg);
99         if (error == OFONO_ERROR_NONE)
100                 INF("%s", title);
101         else
102                 ERR("%s", title);
103 }
104
105 static void _change_pin(const char *what, const char *old, const char *new)
106 {
107         DBG("ask change of %s from %s to %s", what, old, new);
108         ofono_modem_change_pin(what, old, new, _pin_reply, "PIN Change");
109 }
110
111 static void _reset_pin(const char *what, const char *old, const char *new)
112 {
113         DBG("ask reset of %s using %s to %s", what, old, new);
114         ofono_modem_reset_pin(what, old, new, _pin_reply, "PIN Reset");
115 }
116
117 static void _parse_pin(const char *str, char **old, char **new)
118 {
119         const char *p1, *p2, *p3;
120         int len1, len2, len3;
121
122         p1 = strchr(str, '*');
123         if (!p1)
124                 goto error;
125
126         p2 = strchr(p1 + 1, '*');
127         if (!p2)
128                 goto error;
129
130         p3 = strchr(p2 + 1, '#');
131         if (!p3)
132                 goto error;
133
134         len1 = p1 - str;
135         len2 = p2 - (p1 + 1);
136         len3 = p3 - (p2 + 1);
137
138         if ((len2 != len3) || (memcmp(p1 + 1, p2 + 1, len2) != 0)) {
139                 ERR("New PIN code check failed: '%.*s' '%.*s'",
140                         len2, p1 + 1,
141                         len3, p2 + 1);
142                 goto error;
143         }
144
145         *old = strndup(str, len1);
146         *new = strndup(p1 + 1, len2);
147
148         return;
149
150 error:
151         ERR("Invalid PIN change format: %s", str);
152 }
153
154 static Eina_Bool _handle_if_mmi(Keypad *ctx)
155 {
156         const char *str = eina_strbuf_string_get(ctx->number);
157         size_t len = eina_strbuf_length_get(ctx->number);
158
159         if (!str)
160                 return EINA_FALSE;
161
162         if (len < sizeof("*#06#") - 1)
163                 return EINA_FALSE;
164
165         if (str[0] != '*')
166                 return EINA_FALSE;
167         if (str[len - 1] != '#')
168                 return EINA_FALSE;
169
170         DBG("Possible MMI code: %s", str);
171
172         str++;
173         len -= 2;
174
175         if (strcmp(str, "#06#") == 0) {
176                 _imei_show();
177                 eina_strbuf_reset(ctx->number);
178                 _number_display(ctx);
179                 return EINA_TRUE;
180         } else if (strncmp(str, "*0", 2) == 0) {
181                 const char *what = NULL, *p = str + 2;
182                 char *old = NULL, *new = NULL;
183                 void (*action)(const char *, const char *, const char *) = NULL;
184
185                 if (*p == '4') {
186                         /* MMI codes to change PIN:
187                          *  - **04*OLD_PIN*NEW_PIN*NEW_PIN#
188                          *  - **042*OLD-PIN2*NEW_PIN2*NEW_PIN2#
189                          */
190                         p++;
191                         action = _change_pin;
192                         if (*p == '*') {
193                                 what = "pin";
194                                 p++;
195                         } else if (strncmp(p, "2*", 2) == 0) {
196                                 what = "pin2";
197                                 p += 2;
198                         }
199
200                         if (what)
201                                 _parse_pin(p, &old, &new);
202                 } else if (*p == '5') {
203                         /* MMI codes to reset PIN:
204                          *  - **05*PIN_UNBLOCKING_KEY*NEW_PIN*NEW_PIN#
205                          *  - **052*PIN2_UNBLOCKING_KEY*NEW_PIN2*NEW_PIN2#
206                          */
207                         p++;
208                         action = _reset_pin;
209                         if (*p == '*') {
210                                 what = "pin";
211                                 p++;
212                         } else if (strncmp(p, "2*", 2) == 0) {
213                                 what = "pin2";
214                                 p += 2;
215                         }
216
217                         if (what)
218                                 _parse_pin(p, &old, &new);
219                 }
220
221                 DBG("PIN management '%s' what=%s, old=%s, new=%s",
222                         str, what, old, new);
223                 if (action && what && old && new) {
224                         action(what, old, new);
225                         eina_strbuf_reset(ctx->number);
226                         _number_display(ctx);
227                 }
228
229                 free(old);
230                 free(new);
231                 return EINA_TRUE;
232         }
233
234         return EINA_FALSE;
235 }
236
237 static void _dial_reply(void *data, OFono_Error err,
238                         OFono_Call *call __UNUSED__)
239 {
240         Keypad *ctx = data;
241
242         if (err != OFONO_ERROR_NONE) {
243                 char buf[1024];
244                 snprintf(buf, sizeof(buf), "Could not call: %s",
245                                 eina_strbuf_string_get(ctx->number));
246                 gui_simple_popup("Error", buf);
247         }
248 }
249
250 static void _dial(Keypad *ctx)
251 {
252         const char *number = eina_strbuf_string_get(ctx->number);
253
254         INF("call %s", number);
255         ofono_dial(number, NULL, _dial_reply, ctx);
256         eina_stringshare_replace(&(ctx->last), number);
257         eina_strbuf_reset(ctx->number);
258         _number_display(ctx);
259 }
260
261 static void _ss_initiate_reply(void *data, OFono_Error err, const char *str)
262 {
263         Keypad *ctx = data;
264
265         DBG("e=%d, str=%s", err, str);
266         if ((err == OFONO_ERROR_NOT_RECOGNIZED) ||
267                 (err == OFONO_ERROR_INVALID_FORMAT))
268                 _dial(ctx);
269         else if (err == OFONO_ERROR_OFFLINE)
270                 gui_simple_popup("Offline", "System is offline!");
271         else if (err != OFONO_ERROR_NONE) {
272                 char buf[256];
273                 snprintf(buf, sizeof(buf), "Could not complete.<br>Error #%d",
274                                 err);
275                 gui_simple_popup("Error", buf);
276         } else
277                 gui_simple_popup(NULL, str);
278 }
279
280 /* Procedure as ofono/doc/mmi-codes.txt:
281  * - send number to SupplementaryServices.Initiate()
282  * - if NotRecognized is returned, then forward VoiceCallManager.Dial()
283  */
284 static void _call(Keypad *ctx)
285 {
286         const char *number = eina_strbuf_string_get(ctx->number);
287         int len = eina_strbuf_length_get(ctx->number);
288
289         INF("calling %s...", number);
290
291         if ((len > 0) && (number[len - 1] == '#'))
292                 ofono_ss_initiate(number, _ss_initiate_reply, ctx);
293         else
294                 _dial(ctx);
295 }
296
297 static Eina_Bool _on_mod_timeout(void *data)
298 {
299         Keypad *ctx = data;
300         size_t len = eina_strbuf_length_get(ctx->number);
301
302         if (len > 0) {
303                 const char *str = eina_strbuf_string_get(ctx->number);
304                 if (str[len - 1] == '0') {
305                         eina_strbuf_remove(ctx->number, len - 1, len);
306                         eina_strbuf_append_char(ctx->number, '+');
307                         _number_display(ctx);
308                 }
309         }
310
311         ctx->mod_timeout = NULL;
312         return EINA_FALSE;
313 }
314
315 static Eina_Bool _on_rep_timeout(void *data)
316 {
317         Keypad *ctx = data;
318         size_t len = eina_strbuf_length_get(ctx->number);
319
320         if (len > 0) {
321                 eina_strbuf_remove(ctx->number, len - 1, len);
322                 _number_display(ctx);
323         }
324
325         if (len == 1) {
326                 ctx->rep_timeout = NULL;
327                 return EINA_FALSE;
328         }
329
330         ecore_timer_interval_set(ctx->rep_timeout, REP_TIMEOUT);
331         return EINA_TRUE;
332 }
333
334 static void _on_pressed(void *data, Evas_Object *obj __UNUSED__,
335                         const char *emission, const char *source __UNUSED__)
336 {
337         Keypad *ctx = data;
338         DBG("ctx=%p, signal: %s", ctx, emission);
339
340         EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "pressed,"));
341         emission += strlen("pressed,");
342
343         if ((emission[0] >= '0') && (emission[0] <= '9')) {
344                 eina_strbuf_append_char(ctx->number, emission[0]);
345                 _number_display(ctx);
346
347                 if ((emission[0] == '0') &&
348                         (eina_strbuf_length_get(ctx->number) == 1)) {
349                         if (ctx->mod_timeout)
350                                 ecore_timer_del(ctx->mod_timeout);
351                         ctx->mod_timeout = ecore_timer_add(MOD_TIMEOUT,
352                                                 _on_mod_timeout, ctx);
353                 }
354
355         } else if (strcmp(emission, "backspace") == 0) {
356                 size_t len = eina_strbuf_length_get(ctx->number);
357                 if (len > 0) {
358                         eina_strbuf_remove(ctx->number, len - 1, len);
359                         _number_display(ctx);
360                         if (len > 1) {
361                                 if (ctx->rep_timeout)
362                                         ecore_timer_del(ctx->rep_timeout);
363                                 ctx->rep_timeout = ecore_timer_add(
364                                         REP_TIMEOUT_INIT, _on_rep_timeout, ctx);
365                         }
366                 }
367
368         } else if (strcmp(emission, "star") == 0) {
369                 eina_strbuf_append_char(ctx->number, '*');
370                 _number_display(ctx);
371         } else if (strcmp(emission, "hash") == 0) {
372                 eina_strbuf_append_char(ctx->number, '#');
373                 _number_display(ctx);
374         }
375
376         _handle_if_mmi(ctx);
377 }
378
379 static void _on_released(void *data, Evas_Object *obj __UNUSED__,
380                                 const char *emission,
381                                 const char *source __UNUSED__)
382 {
383         Keypad *ctx = data;
384         DBG("ctx=%p, signal: %s", ctx, emission);
385
386         EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "released,"));
387         emission += strlen("released,");
388
389         if (ctx->mod_timeout) {
390                 if (emission[0] != '0')
391                         ERR("Expected '0' but got '%s' instead", emission);
392                 ecore_timer_del(ctx->mod_timeout);
393                 ctx->mod_timeout = NULL;
394         }
395
396         if (ctx->rep_timeout) {
397                 if (strcmp(emission, "backspace") != 0)
398                         ERR("Expected 'backspace' but got '%s' instead",
399                                 emission);
400                 ecore_timer_del(ctx->rep_timeout);
401                 ctx->rep_timeout = NULL;
402         }
403 }
404
405 static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
406                         const char *emission, const char *source __UNUSED__)
407 {
408         Keypad *ctx = data;
409         DBG("ctx=%p, signal: %s", ctx, emission);
410
411         EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "clicked,"));
412         emission += strlen("clicked,");
413
414         if (strcmp(emission, "call") == 0) {
415                 if (eina_strbuf_length_get(ctx->number) > 0)
416                         _call(ctx);
417                 else if (ctx->last) {
418                         eina_strbuf_append(ctx->number, ctx->last);
419                         _number_display(ctx);
420                 }
421         } else if (strcmp(emission, "save") == 0) {
422                 ERR("TODO save contact %s!",
423                         eina_strbuf_string_get(ctx->number));
424         }
425 }
426
427 static void _on_del(void *data, Evas *e __UNUSED__,
428                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
429 {
430         Keypad *ctx = data;
431
432         if (ctx->mod_timeout)
433                 ecore_timer_del(ctx->mod_timeout);
434         if (ctx->rep_timeout)
435                 ecore_timer_del(ctx->rep_timeout);
436
437         eina_strbuf_free(ctx->number);
438         eina_stringshare_del(ctx->last);
439         free(ctx);
440 }
441
442 Evas_Object *keypad_add(Evas_Object *parent)
443 {
444         Keypad *ctx;
445         Evas_Object *obj = gui_layout_add(parent, "keypad");
446         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
447
448         ctx = calloc(1, sizeof(Keypad));
449         ctx->self = obj;
450         ctx->number = eina_strbuf_new();
451
452         evas_object_data_set(obj, "keypad.ctx", ctx);
453
454         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
455                                         _on_del, ctx);
456         elm_object_signal_callback_add(obj, "pressed,*", "keypad",
457                                         _on_pressed, ctx);
458         elm_object_signal_callback_add(obj, "released,*", "keypad",
459                                         _on_released, ctx);
460         elm_object_signal_callback_add(obj, "clicked,*", "keypad",
461                                         _on_clicked, ctx);
462
463         elm_object_part_text_set(obj, "elm.text.display", "");
464         elm_object_part_text_set(ctx->self, "elm.text.contact-and-type", "");
465         elm_object_part_text_set(ctx->self, "elm.text.contact", "");
466         elm_object_part_text_set(ctx->self, "elm.text.phone.type", "");
467         elm_object_signal_emit(obj, "hide,contact", "keypad");
468         elm_object_signal_emit(obj, "disable,save", "keypad");
469         elm_object_signal_emit(obj, "disable,backspace", "keypad");
470
471         elm_object_focus_allow_set(obj, EINA_TRUE);
472
473         return obj;
474 }
475
476 void keypad_number_set(Evas_Object *obj, const char *number,
477                         Eina_Bool auto_dial)
478 {
479         Keypad *ctx;
480         EINA_SAFETY_ON_NULL_RETURN(obj);
481         EINA_SAFETY_ON_NULL_RETURN(number);
482
483         ctx = evas_object_data_get(obj, "keypad.ctx");
484         EINA_SAFETY_ON_NULL_RETURN(ctx);
485
486         eina_strbuf_reset(ctx->number);
487         eina_strbuf_append(ctx->number, number);
488         _number_display(ctx);
489         if (auto_dial)
490                 _call(ctx);
491 }