Implements system and lifecycle events
[platform/core/appfw/appcore-widget.git] / src / widget_app.c
1 /*
2  * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <stdlib.h>
19
20 #include <bundle.h>
21 #include <bundle_internal.h>
22 #include <aul.h>
23 #include <dlog.h>
24 #include <glib.h>
25 #include <glib-object.h>
26 #include <stdlib.h>
27 #include <app_control.h>
28 #include <app_control_internal.h>
29 #include <Elementary.h>
30 #include <widget_errno.h>
31 #include <widget_instance.h>
32 #include <widget_service.h>
33 #include <widget_service_internal.h>
34 #include <aul_app_com.h>
35 #include <Ecore_Wayland.h>
36 #include <system_info.h>
37 #include <vconf.h>
38 #include <vconf-internal-keys.h>
39
40 #include "widget_app.h"
41 #include "widget-log.h"
42 #include "widget-private.h"
43 #include "widget_app_internal.h"
44
45 #ifdef LOG_TAG
46 #undef LOG_TAG
47 #endif
48
49 #define STR_MAX_BUF 128
50 #define LOG_TAG "CAPI_WIDGET_APPLICATION"
51 #define K_REASON    "__WC_K_REASON__"
52
53 typedef enum _widget_obj_state_e {
54         WC_READY = 0,
55         WC_RUNNING = 1,
56         WC_PAUSED = 2,
57         WC_TERMINATED = 3
58 } widget_obj_state_e;
59
60 struct _widget_class {
61         void *user_data;
62         widget_instance_lifecycle_callback_s ops;
63         char *classid;
64         struct _widget_class *next;
65         struct _widget_class *prev;
66 };
67
68 struct app_event_handler {
69         app_event_type_e type;
70         app_event_cb cb;
71         void *data;
72 };
73
74 struct app_event_info {
75         app_event_type_e type;
76         void *value;
77 };
78
79 struct _widget_context {
80         char *id;
81         struct _widget_class *provider;
82         int state;
83         void *tag;
84         Evas_Object *win;
85         int win_id;
86         bundle *content;
87         widget_instance_lifecycle_callback_s ops;
88 };
89
90 typedef struct _widget_class widget_class_s;
91 typedef struct _widget_context widget_context_s;
92
93 #define WIDGET_APP_EVENT_MAX 5
94 static GList *handler_list[WIDGET_APP_EVENT_MAX] = {NULL, };
95
96 static int caller_pid = 0;
97 static widget_app_lifecycle_callback_s *app_ops;
98 static void *app_user_data = NULL;
99 static char *appid = NULL;
100 static widget_class_h class_provider = NULL;
101 static GList *contexts = NULL;
102 static char *viewer_endpoint = NULL;
103
104 static void _widget_core_set_appcore_event_cb(void);
105 static void _widget_core_unset_appcore_event_cb(void);
106
107 static void __free_handler_cb(gpointer data)
108 {
109         if (data)
110                 free(data);
111 }
112
113 static void __free_handler_list(void)
114 {
115         int i;
116
117         for (i = 0; i < WIDGET_APP_EVENT_MAX; i++) {
118                 g_list_free_full(handler_list[i], __free_handler_cb);
119                 handler_list[i] = NULL;
120         }
121 }
122
123 static inline bool _is_widget_feature_enabled(void)
124 {
125         static bool feature = false;
126         static bool retrieved = false;
127         int ret;
128
129         if (retrieved == true)
130                 return feature;
131
132         ret = system_info_get_platform_bool(
133                         "http://tizen.org/feature/shell.appwidget", &feature);
134         if (ret != SYSTEM_INFO_ERROR_NONE) {
135                 _E("failed to get system info");
136                 return false;
137         }
138
139         retrieved = true;
140
141         return feature;
142 }
143
144 static gint __comp_by_id(gconstpointer a, gconstpointer b)
145 {
146         widget_context_s *wc = (widget_context_s *)a;
147
148         return strcmp(wc->id, (const char *)b);
149 }
150
151 static widget_context_s *__find_context_by_id(const char *id)
152 {
153         GList *ret = g_list_find_custom(contexts, id, __comp_by_id);
154
155         if (ret == NULL)
156                 return NULL;
157
158         return ret->data;
159 }
160
161 static gint __comp_by_win(gconstpointer a, gconstpointer b)
162 {
163         int win = GPOINTER_TO_INT(b);
164         widget_context_s *wc = (widget_context_s *)a;
165
166         return (wc && wc->win_id == win) ? 0 : -1;
167 }
168
169 static widget_context_s *__find_context_by_win(int win)
170 {
171         GList *ret = g_list_find_custom(contexts, GINT_TO_POINTER(win), __comp_by_win);
172
173         if (ret == NULL)
174                 return NULL;
175
176         return ret->data;
177 }
178
179 static int __send_lifecycle_event(const char *class_id, const char *instance_id,
180         int status)
181 {
182         bundle *b = bundle_create();
183         int ret;
184
185         if (b == NULL) {
186                 _E("out of memory");
187                 return -1;
188         }
189
190         bundle_add_str(b, WIDGET_K_ID, class_id);
191         bundle_add_str(b, WIDGET_K_INSTANCE, instance_id);
192         bundle_add_byte(b, WIDGET_K_STATUS, &status, sizeof(int));
193
194         _D("send lifecycle %s(%d)", instance_id, status);
195         ret = aul_app_com_send("widget.status", b);
196         if (ret < 0)
197                 _E("send lifecycle error:%d", ret);
198
199         bundle_free(b);
200
201         return ret;
202 }
203
204 static int __send_update_status(const char *class_id, const char *instance_id,
205         int status, bundle *extra, int internal_only)
206 {
207         bundle *b = extra;
208         int lifecycle = -1;
209
210         if (b == NULL)
211                 b = bundle_create();
212
213         bundle_add_str(b, WIDGET_K_ID, class_id);
214         bundle_add_str(b, WIDGET_K_INSTANCE, instance_id);
215         bundle_add_byte(b, WIDGET_K_STATUS, &status, sizeof(int));
216
217         _D("send update %s(%d) to %s", instance_id, status, viewer_endpoint);
218         aul_app_com_send(viewer_endpoint, b);
219
220         switch (status) {
221         case WIDGET_INSTANCE_EVENT_CREATE:
222                 lifecycle = WIDGET_LIFE_CYCLE_EVENT_CREATE;
223                 break;
224         case WIDGET_INSTANCE_EVENT_DESTROY:
225                 lifecycle = WIDGET_LIFE_CYCLE_EVENT_DESTROY;
226                 break;
227         case WIDGET_INSTANCE_EVENT_PAUSE:
228                 lifecycle = WIDGET_LIFE_CYCLE_EVENT_PAUSE;
229                 break;
230         case WIDGET_INSTANCE_EVENT_RESUME:
231                 lifecycle = WIDGET_LIFE_CYCLE_EVENT_RESUME;
232                 break;
233         }
234
235         if (lifecycle > -1)
236                 __send_lifecycle_event(class_id, instance_id, lifecycle);
237
238         if (extra == NULL)
239                 bundle_free(b);
240
241         return 0;
242 }
243
244 static int __instance_resume(widget_class_h handle, const char *id, bundle *b)
245 {
246         widget_context_s *wc = __find_context_by_id(id);
247         int ret;
248
249         if (wc) {
250                 if (handle->ops.resume)
251                         handle->ops.resume(wc, handle->user_data);
252
253                 wc->state = WC_RUNNING;
254                 _D("%s is resumed", id);
255                 ret = __send_update_status(handle->classid, wc->id,
256                         WIDGET_INSTANCE_EVENT_RESUME, NULL, 0);
257         } else {
258                 _E("context not found: %s", id);
259                 ret = -1;
260         }
261
262         return ret;
263 }
264
265 static int __instance_pause(widget_class_h handle, const char *id, bundle *b)
266 {
267         widget_context_s *wc = __find_context_by_id(id);
268         int ret;
269
270         if (wc) {
271                 if (handle->ops.pause)
272                         handle->ops.pause(wc, handle->user_data);
273
274                 wc->state = WC_PAUSED;
275                 _D("%s is paused", id);
276                 ret = __send_update_status(handle->classid, wc->id,
277                         WIDGET_INSTANCE_EVENT_PAUSE, NULL, 0);
278         } else {
279                 _E("context not found: %s", id);
280                 ret = -1;
281         }
282
283         return ret;
284 }
285
286 static int __instance_resize(widget_class_h handle, const char *id, bundle *b)
287 {
288         widget_context_s *wc = __find_context_by_id(id);
289         int ret;
290         int w;
291         int h;
292         char *w_str = NULL;
293         char *h_str = NULL;
294         char *remain = NULL;
295
296         if (wc) {
297                 bundle_get_str(b, WIDGET_K_WIDTH, &w_str);
298                 bundle_get_str(b, WIDGET_K_HEIGHT, &h_str);
299
300                 if (w_str)
301                         w = (int)g_ascii_strtoll(w_str, &remain, 10);
302                 else
303                         w = -1;
304
305                 if (h_str)
306                         h = (int)g_ascii_strtoll(h_str, &remain, 10);
307                 else
308                         h = -1;
309
310                 if (handle->ops.resize)
311                         handle->ops.resize(wc, w, h, handle->user_data);
312                 _D("%s is resized to %dx%d", id, w, h);
313                 ret = __send_update_status(handle->classid, wc->id,
314                         WIDGET_INSTANCE_EVENT_SIZE_CHANGED, NULL, 0);
315         } else {
316                 _E("context not found: %s", id);
317                 ret = -1;
318         }
319
320         return ret;
321 }
322
323 static int __instance_update(widget_class_h handle, const char *id, bundle *b)
324 {
325         widget_context_s *wc = __find_context_by_id(id);
326         int ret;
327         int force;
328         char *force_str = NULL;
329
330         if (!wc) {
331                 _E("context not found: %s", id);
332                 return -1;
333         }
334
335         if (handle->ops.update) {
336                 if (b)
337                         bundle_get_str(b, WIDGET_K_FORCE, &force_str);
338
339                 if (force_str && strcmp(force_str, "true") == 0)
340                         force = 1;
341                 else
342                         force = 0;
343
344                 handle->ops.update(wc, b, force, handle->user_data);
345                 ret = __send_update_status(handle->classid, wc->id,
346                         WIDGET_INSTANCE_EVENT_UPDATE, b, 0);
347                 _D("updated:%s", id);
348         }
349
350         return ret;
351 }
352
353 static int __instance_create(widget_class_h handle, const char *id, bundle *b)
354 {
355         widget_context_s *wc = NULL;
356         int w = 0, h = 0;
357         char *w_str = NULL, *h_str = NULL;
358         char *remain = NULL;
359         int ret = 0;
360
361         wc = (widget_context_s *)malloc(sizeof(widget_context_s));
362         if (!wc)
363                 return WIDGET_ERROR_OUT_OF_MEMORY;
364
365         wc->state = WC_READY;
366         wc->id = g_strdup(id);
367         wc->provider = handle;
368         wc->win = NULL;
369         wc->win_id = -1;
370
371         wc->content = bundle_dup(b);
372         bundle_get_str(b, WIDGET_K_WIDTH, &w_str);
373         bundle_get_str(b, WIDGET_K_HEIGHT, &h_str);
374
375         if (w_str)
376                 w = (int)g_ascii_strtoll(w_str, &remain, 10);
377
378         if (h_str)
379                 h = (int)g_ascii_strtoll(h_str, &remain, 10);
380
381         contexts = g_list_append(contexts, wc);
382
383         handle->ops.create(wc, b, w, h, handle->user_data);
384         ret = __send_update_status(handle->classid, wc->id,
385                         WIDGET_INSTANCE_EVENT_CREATE, b, 0);
386
387         return ret;
388 }
389
390 static int __instance_destroy(widget_class_h handle, const char *id,
391                 widget_destroy_type_e reason, bundle *b)
392 {
393         widget_context_s *wc = __find_context_by_id(id);
394         int ret = 0;
395
396         if (wc) {
397                 wc->state = WC_TERMINATED;
398                 handle->ops.destroy(wc, (widget_app_destroy_type_e)reason, b,
399                                 handle->user_data);
400
401                 ret = __send_update_status(handle->classid, id,
402                                 WIDGET_INSTANCE_EVENT_TERMINATE, b, 0);
403
404                 contexts = g_list_remove(contexts, wc);
405
406                 if (wc->id)
407                         free(wc->id);
408                 free(wc);
409         } else {
410                 _E("could not find widget obj: %s", id);
411                 ret = WIDGET_ERROR_INVALID_PARAMETER;
412         }
413
414         return ret;
415 }
416
417 static widget_class_h __find_class_handler(const char *class_id,
418                 widget_class_h handle)
419 {
420         if (!class_id || !handle)
421                 return NULL;
422
423         widget_class_h head = handle;
424
425         while (head) {
426                 if (head->classid && strcmp(head->classid, class_id) == 0)
427                         return head;
428
429                 head = head->next;
430         }
431
432         return NULL;
433 }
434
435 static void __control(bundle *b)
436 {
437         char *class_id = NULL;
438         char *id = NULL;
439         char *operation = NULL;
440         char *reason = NULL;
441         char *remain = NULL;
442         int destroy_type = WIDGET_DESTROY_TYPE_DEFAULT;
443
444         widget_class_h handle = NULL;
445         bundle_get_str(b, WIDGET_K_CLASS, &class_id);
446         /* for previous version compatibility, use appid for default class id */
447         if (class_id == NULL)
448                 class_id = appid;
449
450         bundle_get_str(b, WIDGET_K_INSTANCE, &id);
451         bundle_get_str(b, WIDGET_K_OPERATION, &operation);
452
453         handle = __find_class_handler(class_id, class_provider);
454         if (!handle) {
455                 _E("no handle provided: %s", class_id);
456                 goto error;
457         }
458
459         if (!operation) {
460                 _E("no operation provided");
461                 goto error;
462         }
463
464         if (strcmp(operation, "create") == 0) {
465                 __instance_create(handle, id, b);
466         } else if (strcmp(operation, "resize") == 0) {
467                 __instance_resize(handle, id, b);
468         } else if (strcmp(operation, "update") == 0) {
469                 __instance_update(handle, id, b);
470         } else if (strcmp(operation, "destroy") == 0) {
471                 bundle_get_str(b, WIDGET_K_REASON, &reason);
472                 if (reason)
473                         destroy_type = (int)g_ascii_strtoll(reason, &remain,
474                                         10);
475
476                 __instance_destroy(handle, id, destroy_type, b);
477         } else if (strcmp(operation, "resume") == 0) {
478                 __instance_resume(handle, id, b);
479         } else if (strcmp(operation, "pause") == 0) {
480                 __instance_pause(handle, id, b);
481         }
482
483         return;
484 error:
485         LOGD("error on control");
486         return;
487 }
488
489 static void __resume_cb(const char *id, void *data)
490 {
491         widget_context_s *cxt = __find_context_by_id(id);
492
493         if (cxt)
494                 __instance_resume(cxt->provider, id, NULL);
495         else
496                 _E("invalid context id:%s", id);
497
498 }
499
500 static void __pause_cb(const char *id, void *data)
501 {
502         widget_context_s *cxt = __find_context_by_id(id);
503
504         if (cxt)
505                 __instance_pause(cxt->provider, id, NULL);
506         else
507                 _E("invalid context id:%s", id);
508 }
509
510 static void __pause_all()
511 {
512         GList *iter = g_list_first(contexts);
513
514         while (iter != NULL) {
515                 widget_context_s *cxt = (widget_context_s *)iter->data;
516                 const char *id = cxt->id;
517
518                 switch (cxt->state) {
519                 case WC_READY:
520                         __resume_cb(id, NULL);
521                         __pause_cb(id, NULL);
522                         break;
523                 case WC_RUNNING:
524                         __pause_cb(id, NULL);
525                         break;
526                 }
527                 iter = g_list_next(iter);
528         }
529 }
530
531 static void __resume_all()
532 {
533         GList *iter = g_list_first(contexts);
534
535         while (iter != NULL) {
536                 widget_context_s *cxt = (widget_context_s *)iter->data;
537                 const char *id = cxt->id;
538
539                 switch (cxt->state) {
540                 case WC_READY:
541                         __resume_cb(id, NULL);
542                         break;
543                 case WC_PAUSED:
544                         __resume_cb(id, NULL);
545                         break;
546                 }
547                 iter = g_list_next(iter);
548         }
549 }
550
551 static Eina_Bool __show_cb(void *data, int type, void *event)
552 {
553         Ecore_Wl_Event_Window_Show *ev = event;
554         widget_context_s *cxt = __find_context_by_win(ev->win);
555
556         LOGD("show %d %d", (unsigned int)ev->win, (unsigned int)ev->data[0]);
557
558         if (cxt)
559                 __instance_resume(cxt->provider, cxt->id, NULL);
560         else
561                 LOGE("unknown window error: %d", ev->win);
562
563         return ECORE_CALLBACK_RENEW;
564 }
565
566 static Eina_Bool __hide_cb(void *data, int type, void *event)
567 {
568         Ecore_Wl_Event_Window_Hide *ev = event;
569         widget_context_s *cxt = __find_context_by_win(ev->win);
570
571
572         LOGD("hide %d", (unsigned int)ev->win);
573
574         if (cxt)
575                 __instance_pause(cxt->provider, cxt->id, NULL);
576         else
577                 LOGE("unknown window error: %d", ev->win);
578
579         return ECORE_CALLBACK_RENEW;
580 }
581
582 static Eina_Bool __visibility_cb(void *data, int type, void *event)
583 {
584         Ecore_Wl_Event_Window_Visibility_Change *ev = event;
585         LOGD("visiblity change: %d %d", (unsigned int)ev->win,  (unsigned int)ev->fully_obscured);
586         /* this is not working so far*/
587         return ECORE_CALLBACK_RENEW;
588 }
589
590 static Eina_Bool __lower_cb(void *data, int type, void *event)
591 {
592         LOGD("lower");
593         return ECORE_CALLBACK_RENEW;
594 }
595
596 static void __add_climsg()
597 {
598         ecore_event_handler_add(ECORE_WL_EVENT_WINDOW_SHOW, __show_cb, NULL);
599         ecore_event_handler_add(ECORE_WL_EVENT_WINDOW_HIDE, __hide_cb, NULL);
600         ecore_event_handler_add(ECORE_WL_EVENT_WINDOW_VISIBILITY_CHANGE, __visibility_cb, NULL);
601         ecore_event_handler_add(ECORE_WL_EVENT_WINDOW_LOWER, __lower_cb, NULL);
602 }
603
604 static int __aul_handler(aul_type type, bundle *b, void *data)
605 {
606         char *caller = NULL;
607         char *remain = NULL;
608
609         switch (type) {
610         case AUL_START:
611                 if (b) {
612                         bundle_get_str(b, WIDGET_K_CALLER, &caller);
613                         if (caller) {
614                                 caller_pid = g_ascii_strtoll(caller, &remain,
615                                                 10);
616                         } else {
617                                 /* using caller appid and query pid using caller appid? */
618                                 _E("no caller pid");
619                         }
620                 }
621
622                 __control(b);
623                 break;
624         case AUL_RESUME:
625                 __resume_all();
626                 break;
627         case AUL_TERMINATE:
628                 widget_app_exit();
629                 break;
630         default:
631                 break;
632         }
633
634         return 0;
635 }
636
637 static char *__get_domain_name(char *appid)
638 {
639         char *name_token;
640
641         if (appid == NULL) {
642                 _E("appid is NULL");
643                 return NULL;
644         }
645
646         name_token = strrchr(appid, '.');
647
648         if (name_token == NULL) {
649                 _E("appid is invalid");
650                 return appid;
651         }
652
653         name_token++;
654
655         return name_token;
656 }
657
658 static void __on_poweroff(keynode_t *key, void *data)
659 {
660         int val;
661
662         val = vconf_keynode_get_int(key);
663         switch (val) {
664         case VCONFKEY_SYSMAN_POWER_OFF_DIRECT:
665         case VCONFKEY_SYSMAN_POWER_OFF_RESTART:
666                 _I("power off changed: %d", val);
667                 widget_app_exit();
668                 break;
669         case VCONFKEY_SYSMAN_POWER_OFF_NONE:
670         case VCONFKEY_SYSMAN_POWER_OFF_POPUP:
671         default:
672                 /* DO NOTHING */
673                 break;
674         }
675 }
676
677 extern int _set_i18n(const char *name);
678
679 static int __before_loop(int argc, char **argv)
680 {
681         int r;
682         bundle *kb = NULL;
683         char *wayland_display = NULL;
684         char *xdg_runtime_dir = NULL;
685         char *name;
686
687 #if !(GLIB_CHECK_VERSION(2, 36, 0))
688         g_type_init();
689 #endif
690
691         kb = bundle_import_from_argv(argc, argv);
692         if (kb) {
693                 bundle_get_str(kb, AUL_K_WAYLAND_WORKING_DIR, &xdg_runtime_dir);
694                 bundle_get_str(kb, AUL_K_WAYLAND_DISPLAY, &wayland_display);
695                 bundle_get_str(kb, WIDGET_K_ENDPOINT, &viewer_endpoint);
696                 if (viewer_endpoint) {
697                         _E("viewer endpoint :%s", viewer_endpoint);
698                         viewer_endpoint = strdup(viewer_endpoint);
699                 } else {
700                         _E("endpoint is missing");
701                 }
702
703                 if (xdg_runtime_dir)
704                         setenv("XDG_RUNTIME_DIR", xdg_runtime_dir, 1);
705
706                 if (wayland_display)
707                         setenv("WAYLAND_DISPLAY", wayland_display, 1);
708
709                 bundle_free(kb);
710                 kb = NULL;
711         } else {
712                 _E("failed to get launch argv");
713         }
714
715         elm_init(argc, argv);
716
717         r = aul_launch_init(__aul_handler, NULL);
718         if (r < 0) {
719                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
720                                 __FUNCTION__,
721                                 "Fail to call the aul_launch_init");
722         }
723
724         r = aul_launch_argv_handler(argc, argv);
725         if (r < 0) {
726                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
727                                 __FUNCTION__,
728                                 "Fail to call the aul_launch_argv_handler");
729         }
730
731         r = app_get_id(&appid);
732         if (r != APP_ERROR_NONE)
733                 return r;
734
735         name = __get_domain_name(appid);
736
737         if (name == NULL) {
738                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
739                                 __FUNCTION__,
740                                 "Fail to call __get_domain_name");
741         }
742
743         r = _set_i18n(name);
744
745         if (r < 0) {
746                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
747                                 __FUNCTION__,
748                                 "Fail to call _set_i18n");
749         }
750
751         __add_climsg();
752
753         _widget_core_set_appcore_event_cb();
754
755         class_provider = app_ops->create(app_user_data);
756         if (class_provider == NULL) {
757                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
758                                 __FUNCTION__, "widget_class is NULL");
759         }
760
761         vconf_notify_key_changed(VCONFKEY_SYSMAN_POWER_OFF_STATUS, __on_poweroff, NULL);
762
763         return WIDGET_ERROR_NONE;
764 }
765
766 static void __after_loop()
767 {
768         vconf_ignore_key_changed(VCONFKEY_SYSMAN_POWER_OFF_STATUS, __on_poweroff);
769
770         __pause_all();
771
772         if (app_ops->terminate)
773                 app_ops->terminate(app_user_data);
774
775         if (viewer_endpoint)
776                 free(viewer_endpoint);
777
778         _widget_core_unset_appcore_event_cb();
779         __free_handler_list();
780         elm_shutdown();
781 }
782
783 static void __on_low_memory(keynode_t *key, void *data)
784 {
785         int val;
786
787         val = vconf_keynode_get_int(key);
788         if (val == VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING) {
789                 app_event_handler_h handler;
790                 struct app_event_info event;
791
792                 _I("widget_app_low_memory");
793
794                 event.type = APP_EVENT_LOW_MEMORY;
795                 event.value = (void *)&val;
796
797                 GList *iter = g_list_first(handler_list[APP_EVENT_LOW_MEMORY]);
798
799                 while (iter) {
800                         handler = (app_event_handler_h) iter->data;
801                         handler->cb(&event, handler->data);
802                         iter = g_list_next(iter);
803                 }
804         }
805 }
806
807 static void __on_low_battery(keynode_t *key, void *data)
808 {
809         int val;
810
811         val = vconf_keynode_get_int(key);
812         if (val <= VCONFKEY_SYSMAN_BAT_CRITICAL_LOW) {
813                 app_event_handler_h handler;
814                 struct app_event_info event;
815
816                 _I("widget_app_low_battery");
817
818                 event.type = APP_EVENT_LOW_BATTERY;
819                 event.value = (void *)&val;
820
821                 GList *iter = g_list_first(handler_list[APP_EVENT_LOW_BATTERY]);
822
823                 while (iter) {
824                         handler = (app_event_handler_h) iter->data;
825                         handler->cb(&event, handler->data);
826                         iter = g_list_next(iter);
827                 }
828         }
829 }
830
831 static void __on_lang_changed(keynode_t *key, void *data)
832 {
833         char *val;
834
835         _update_lang();
836         val = vconf_keynode_get_str(key);
837
838         app_event_handler_h handler;
839         struct app_event_info event;
840
841         _I("widget_app_lang_changed");
842
843         event.type = APP_EVENT_LANGUAGE_CHANGED;
844         event.value = (void *)val;
845
846         GList *iter = g_list_first(handler_list[APP_EVENT_LANGUAGE_CHANGED]);
847
848         while (iter) {
849                 handler = (app_event_handler_h) iter->data;
850                 handler->cb(&event, handler->data);
851                 iter = g_list_next(iter);
852         }
853 }
854
855 static void __on_region_changed(keynode_t *key, void *data)
856 {
857         char *val;
858
859         _update_region();
860         val = vconf_keynode_get_str(key);
861
862         app_event_handler_h handler;
863         struct app_event_info event;
864
865         _I("widget_app_region_changed");
866
867         event.type = APP_EVENT_REGION_FORMAT_CHANGED;
868         event.value = (void *)val;
869
870         GList *iter = g_list_first(handler_list[APP_EVENT_REGION_FORMAT_CHANGED]);
871
872         while (iter) {
873                 handler = (app_event_handler_h) iter->data;
874                 handler->cb(&event, handler->data);
875                 iter = g_list_next(iter);
876         }
877 }
878
879 static void __register_event(int event_type)
880 {
881         switch (event_type) {
882         case APP_EVENT_LOW_MEMORY:
883                 vconf_notify_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory, NULL);
884                 break;
885
886         case APP_EVENT_LOW_BATTERY:
887                 vconf_notify_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery, NULL);
888                 break;
889
890         case APP_EVENT_LANGUAGE_CHANGED:
891                 vconf_notify_key_changed(VCONFKEY_LANGSET, __on_lang_changed, NULL);
892                 break;
893
894         case APP_EVENT_REGION_FORMAT_CHANGED:
895                 vconf_notify_key_changed(VCONFKEY_REGIONFORMAT, __on_region_changed, NULL);
896                 break;
897         }
898 }
899
900 static void __unregister_event(int event_type)
901 {
902         switch (event_type) {
903         case APP_EVENT_LOW_MEMORY:
904                 vconf_ignore_key_changed(VCONFKEY_SYSMAN_LOW_MEMORY, __on_low_memory);
905                 break;
906
907         case APP_EVENT_LOW_BATTERY:
908                 vconf_ignore_key_changed(VCONFKEY_SYSMAN_BATTERY_STATUS_LOW, __on_low_battery);
909                 break;
910
911         case APP_EVENT_LANGUAGE_CHANGED:
912                 vconf_ignore_key_changed(VCONFKEY_LANGSET, __on_lang_changed);
913                 break;
914
915         case APP_EVENT_REGION_FORMAT_CHANGED:
916                 vconf_ignore_key_changed(VCONFKEY_REGIONFORMAT, __on_region_changed);
917                 break;
918         }
919 }
920
921 static void _widget_core_set_appcore_event_cb(void)
922 {
923         __register_event(APP_EVENT_LANGUAGE_CHANGED);
924         __register_event(APP_EVENT_REGION_FORMAT_CHANGED);
925 }
926
927 static void _widget_core_unset_appcore_event_cb(void)
928 {
929         __unregister_event(APP_EVENT_LANGUAGE_CHANGED);
930         __unregister_event(APP_EVENT_REGION_FORMAT_CHANGED);
931 }
932
933 EXPORT_API int widget_app_main(int argc, char **argv,
934                 widget_app_lifecycle_callback_s *callback, void *user_data)
935 {
936         int r;
937
938         if (!_is_widget_feature_enabled()) {
939                 _E("not supported");
940                 return WIDGET_ERROR_NOT_SUPPORTED;
941         }
942
943         if (argc <= 0 || argv == NULL || callback == NULL)
944                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
945                                 __FUNCTION__, NULL);
946
947         if (callback->create == NULL)
948                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
949                                 __FUNCTION__,
950                                 "widget_app_create_cb() callback must be "
951                                 "registered");
952
953         app_ops = callback;
954         app_user_data = user_data;
955         r = __before_loop(argc, argv);
956         if (r < 0)
957                 return r;
958
959         ecore_main_loop_begin();
960         aul_status_update(STATUS_DYING);
961         __after_loop();
962
963         return WIDGET_ERROR_NONE;
964 }
965
966 EXPORT_API int widget_app_exit(void)
967 {
968         if (!_is_widget_feature_enabled()) {
969                 _E("not supported");
970                 return WIDGET_ERROR_NOT_SUPPORTED;
971         }
972
973         ecore_main_loop_quit();
974
975         return WIDGET_ERROR_NONE;
976 }
977
978 static gboolean __finish_event_cb(gpointer user_data)
979 {
980         if (user_data == NULL)
981                 return FALSE;
982
983         widget_context_s *wc = (widget_context_s *)user_data;
984
985         switch (wc->state) {
986         case WC_READY:
987
988                 break;
989         case WC_RUNNING:
990
991                 break;
992         case WC_PAUSED:
993
994                 break;
995         default:
996                 break;
997         }
998
999         return FALSE;
1000 }
1001
1002 EXPORT_API int widget_app_terminate_context(widget_context_h context)
1003 {
1004         if (!_is_widget_feature_enabled()) {
1005                 _E("not supported");
1006                 return WIDGET_ERROR_NOT_SUPPORTED;
1007         }
1008
1009         if (context == NULL)
1010                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1011                                 __FUNCTION__, NULL);
1012
1013         g_idle_add(__finish_event_cb, context);
1014         return WIDGET_ERROR_NONE;
1015 }
1016
1017 EXPORT_API int widget_app_foreach_context(widget_context_cb cb, void *data)
1018 {
1019         GList *list;
1020         widget_context_s *wc;
1021
1022         if (!_is_widget_feature_enabled()) {
1023                 _E("not supported");
1024                 return WIDGET_ERROR_NOT_SUPPORTED;
1025         }
1026
1027         if (!cb)
1028                 return WIDGET_ERROR_INVALID_PARAMETER;
1029
1030         list = g_list_first(contexts);
1031
1032         while (list) {
1033                 wc = (widget_context_s *)list->data;
1034                 if (wc) {
1035                         if (!cb(wc, data))
1036                                 break;
1037                 }
1038                 list = list->next;
1039         }
1040
1041         return WIDGET_ERROR_NONE;
1042 }
1043
1044 EXPORT_API int widget_app_add_event_handler(app_event_handler_h *event_handler,
1045                                         app_event_type_e event_type, app_event_cb callback,
1046                                         void *user_data)
1047 {
1048         int r;
1049         bool feature;
1050
1051         r = system_info_get_platform_bool(FEATURE_SHELL_APPWIDGET, &feature);
1052         if (r < 0)
1053                 return WIDGET_ERROR_FAULT;
1054
1055         if (!feature)
1056                 return WIDGET_ERROR_NOT_SUPPORTED;
1057
1058         app_event_handler_h handler;
1059
1060         if (event_handler == NULL || callback == NULL)
1061                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
1062
1063         if (event_type < APP_EVENT_LOW_MEMORY
1064             || event_type > APP_EVENT_REGION_FORMAT_CHANGED)
1065                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
1066
1067         if (event_type == APP_EVENT_DEVICE_ORIENTATION_CHANGED)
1068                 return widget_app_error(WIDGET_ERROR_NOT_SUPPORTED, __FUNCTION__, NULL);
1069
1070         GList *iter = g_list_first(handler_list[event_type]);
1071
1072         while (iter) {
1073                 handler = (app_event_handler_h) iter->data;
1074
1075                 if (handler->cb == callback)
1076                         return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
1077
1078                 iter = g_list_next(iter);
1079         }
1080
1081         handler = calloc(1, sizeof(struct app_event_handler));
1082         if (!handler)
1083                 return widget_app_error(WIDGET_ERROR_OUT_OF_MEMORY, __FUNCTION__, NULL);
1084
1085         if (g_list_length(handler_list[event_type]) == 0)
1086                 __register_event(event_type);
1087
1088         handler->type = event_type;
1089         handler->cb = callback;
1090         handler->data = user_data;
1091         handler_list[event_type] = g_list_append(handler_list[event_type], handler);
1092
1093         *event_handler = handler;
1094
1095         return WIDGET_ERROR_NONE;
1096 }
1097
1098 EXPORT_API int widget_app_remove_event_handler(app_event_handler_h
1099                                                 event_handler)
1100 {
1101         int r;
1102         bool feature;
1103
1104         r = system_info_get_platform_bool(FEATURE_SHELL_APPWIDGET, &feature);
1105         if (r < 0)
1106                 return WIDGET_ERROR_FAULT;
1107
1108         if (!feature)
1109                 return WIDGET_ERROR_NOT_SUPPORTED;
1110
1111         app_event_type_e type;
1112
1113         if (event_handler == NULL)
1114                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
1115
1116         type = event_handler->type;
1117         if (type < APP_EVENT_LOW_MEMORY || type > APP_EVENT_REGION_FORMAT_CHANGED)
1118                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL);
1119
1120         handler_list[type] = g_list_remove(handler_list[type], event_handler);
1121         free(event_handler);
1122
1123         if (g_list_length(handler_list[type]) == 0)
1124                 __unregister_event(type);
1125
1126         return WIDGET_ERROR_NONE;
1127 }
1128
1129 EXPORT_API const char *widget_app_get_id(widget_context_h context)
1130 {
1131         if (!_is_widget_feature_enabled()) {
1132                 _E("not supported");
1133                 set_last_result(WIDGET_ERROR_NOT_SUPPORTED);
1134                 return NULL;
1135         }
1136
1137         if (!context) {
1138                 set_last_result(WIDGET_ERROR_INVALID_PARAMETER);
1139                 return NULL;
1140         }
1141
1142         set_last_result(WIDGET_ERROR_NONE);
1143         return context->id;
1144 }
1145
1146 EXPORT_API int widget_app_get_elm_win(widget_context_h context,
1147                                         Evas_Object **win)
1148 {
1149         widget_context_s *cxt = (widget_context_s *)context;
1150         Evas_Object *ret_win;
1151         Ecore_Wl_Window *wl_win;
1152
1153         if (!_is_widget_feature_enabled()) {
1154                 _E("not supported");
1155                 return WIDGET_ERROR_NOT_SUPPORTED;
1156         }
1157
1158         if (context == NULL || win == NULL)
1159                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1160                                 __FUNCTION__, NULL);
1161
1162         ret_win = elm_win_add(NULL, cxt->id, ELM_WIN_BASIC);
1163         if (ret_win == NULL) {
1164                 _E("failed to create window");
1165                 return WIDGET_ERROR_FAULT;
1166         }
1167
1168         wl_win = elm_win_wl_window_get(ret_win);
1169         if (wl_win == NULL) {
1170                 _E("failed to get wayland window");
1171                 evas_object_del(ret_win);
1172                 return WIDGET_ERROR_FAULT;
1173         }
1174
1175         ecore_wl_window_class_name_set(wl_win, cxt->id);
1176
1177         *win = ret_win;
1178         cxt->win = ret_win;
1179         cxt->win_id = ecore_wl_window_id_get(wl_win);
1180
1181         _D("window created: %d", cxt->win_id);
1182
1183         return WIDGET_ERROR_NONE;
1184 }
1185
1186 widget_class_h _widget_class_create(widget_class_s *prev, const char *class_id,
1187                 widget_instance_lifecycle_callback_s callback, void *user_data)
1188 {
1189         widget_class_s *wc;
1190
1191         if (!_is_widget_feature_enabled()) {
1192                 _E("not supported");
1193                 set_last_result(WIDGET_ERROR_NOT_SUPPORTED);
1194                 return NULL;
1195         }
1196
1197         if (class_id == NULL) {
1198                 set_last_result(WIDGET_ERROR_INVALID_PARAMETER);
1199                 return NULL;
1200         }
1201
1202         wc = (widget_class_s *)malloc(sizeof(widget_class_s));
1203         if (wc == NULL) {
1204                 _E("failed to malloc : %s", __FUNCTION__);
1205                 set_last_result(WIDGET_ERROR_OUT_OF_MEMORY);
1206                 return NULL;
1207         }
1208
1209         wc->classid = strdup(class_id);
1210         wc->user_data = user_data;
1211         wc->ops = callback;
1212         wc->next = prev;
1213         wc->prev = NULL;
1214
1215         set_last_result(WIDGET_ERROR_NONE);
1216
1217         if (prev)
1218                 prev->prev = wc;
1219
1220         return wc;
1221 }
1222
1223 EXPORT_API widget_class_h widget_app_class_add(widget_class_h widget_class,
1224                 const char *class_id,
1225                 widget_instance_lifecycle_callback_s callback, void *user_data)
1226 {
1227         return _widget_class_create(widget_class, class_id, callback,
1228                         user_data);
1229 }
1230
1231 EXPORT_API widget_class_h widget_app_class_create(
1232                 widget_instance_lifecycle_callback_s callback, void *user_data)
1233 {
1234         return _widget_class_create(class_provider, appid, callback, user_data);
1235 }
1236
1237 EXPORT_API int widget_app_context_set_tag(widget_context_h context, void *tag)
1238 {
1239         if (!_is_widget_feature_enabled()) {
1240                 _E("not supported");
1241                 return WIDGET_ERROR_NOT_SUPPORTED;
1242         }
1243
1244         if (context == NULL)
1245                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1246                                 __FUNCTION__, NULL);
1247
1248         context->tag = tag;
1249
1250         return WIDGET_ERROR_NONE;
1251 }
1252
1253 EXPORT_API int widget_app_context_get_tag(widget_context_h context, void **tag)
1254 {
1255         if (!_is_widget_feature_enabled()) {
1256                 _E("not supported");
1257                 return WIDGET_ERROR_NOT_SUPPORTED;
1258         }
1259
1260         if (context == NULL || tag == NULL)
1261                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1262                                 __FUNCTION__, NULL);
1263
1264         *tag = context->tag;
1265
1266         return WIDGET_ERROR_NONE;
1267 }
1268
1269 EXPORT_API int widget_app_context_set_content_info(widget_context_h context,
1270                 bundle *content_info)
1271 {
1272         const char *class_id = NULL;
1273         int ret = 0;
1274
1275         if (!_is_widget_feature_enabled()) {
1276                 _E("not supported");
1277                 return WIDGET_ERROR_NOT_SUPPORTED;
1278         }
1279
1280         if (context == NULL || content_info == NULL)
1281                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1282                                 __FUNCTION__, NULL);
1283
1284         if (context->provider == NULL)
1285                 return widget_app_error(WIDGET_ERROR_INVALID_PARAMETER,
1286                                 __FUNCTION__, NULL);
1287
1288         class_id = context->provider->classid;
1289
1290         if (class_id == NULL)
1291                 return widget_app_error(WIDGET_ERROR_FAULT, __FUNCTION__, NULL);
1292
1293         ret = __send_update_status(class_id, context->id,
1294                         WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, content_info, true);
1295
1296         if (ret < 0) {
1297                 _E("failed to send content info: %s of %s (%d)", context->id,
1298                                 class_id, ret);
1299                 return widget_app_error(WIDGET_ERROR_IO_ERROR, __FUNCTION__,
1300                                 NULL);
1301         }
1302
1303         return WIDGET_ERROR_NONE;
1304 }
1305
1306 EXPORT_API int widget_app_context_set_title(widget_context_h context,
1307                 const char *title)
1308 {
1309         if (!_is_widget_feature_enabled()) {
1310                 _E("not supported");
1311                 return WIDGET_ERROR_NOT_SUPPORTED;
1312         }
1313
1314         if (!context || !title)
1315                 return WIDGET_ERROR_INVALID_PARAMETER;
1316
1317         if (context->win)
1318                 elm_win_title_set(context->win, title);
1319
1320         return WIDGET_ERROR_NONE;
1321 }
1322