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