20c10e1f3d834a4f0617f76e3a340334a40c9013
[platform/core/appfw/appcore-widget.git] / src / base / widget_base.cc
1 /*
2  * Copyright (c) 2021 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 #include <aul.h>
18 #include <aul_app_com.h>
19 #include <aul_widget.h>
20 #include <bundle_internal.h>
21 #include <dlog.h>
22 #include <glib.h>
23 #include <screen_connector_provider.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <widget_errno.h>
27 #include <widget_instance.h>
28
29 #include <stdexcept>
30
31 #include <app_core_multi_window_base.hh>
32
33 #include "common/log_private.hh"
34 #include "include/widget_base.hh"
35
36 namespace tizen_cpp {
37 namespace {
38
39 constexpr const char kStatusForeground[] = "fg";
40 constexpr const char kStatusBackground[] = "bg";
41
42 class AppContext {
43  public:
44   void SetAppId(const std::string& app_id) {
45     app_id_ = app_id;
46   }
47
48 /* LCOV_EXCL_START */
49   const std::string& GetAppId() const {
50     return app_id_;
51   }
52 /* LCOV_EXCL_STOP */
53
54   void SetPackageId(const std::string& package_id) {
55     package_id_ = package_id;
56   }
57
58   const std::string& GetPackageId() const {
59     return package_id_;
60   }
61
62   void SetViewerEndpoint(const std::string& viewer_endpoint) {
63     viewer_endpoint_ = viewer_endpoint;
64   }
65
66   const std::string& GetViewerEndpoint() const {
67     return viewer_endpoint_;
68   }
69
70   void SetPermanent(bool permanent) {
71     permanent_ = permanent;
72   }
73
74   bool IsPermanent() const {
75     return permanent_;
76   }
77 /* LCOV_EXCL_START */
78   void SetFgSignal(bool fg_signal) {
79     fg_signal_ = fg_signal;
80   }
81
82   bool IsFgSignal() const {
83     return fg_signal_;
84   }
85 /* LCOV_EXCL_STOP */
86
87   int SendUpdateStatus(const std::string& class_id,
88       const std::string& instance_id, int status, int err,
89       bundle* extra) {
90     int ret = aul_widget_send_status_to_viewer(class_id.c_str(),
91         instance_id.c_str(), viewer_endpoint_.c_str(), status, err, extra);
92     if (ret != AUL_R_OK)
93       return ret;
94
95     int lifecycle = widget_instance_convert_event_to_lifecycle_status(status);
96     if (lifecycle > -1) {
97       ret = aul_widget_send_status_to_service(class_id.c_str(),
98           instance_id.c_str(), package_id_.c_str(), lifecycle);
99     }
100
101     return ret;
102   }
103
104  private:
105   std::string app_id_;
106   std::string package_id_;
107   std::string viewer_endpoint_;
108   bool permanent_ = false;
109   bool fg_signal_ = false;
110 };
111
112 AppContext __context;
113
114 }  // namespace
115
116 class WidgetBase::Impl {
117  public:
118   explicit Impl(WidgetBase* parent) : parent_(parent) {}
119
120  private:
121   friend class WidgetBase;
122   WidgetBase* parent_;
123
124   void ControlCreate(const std::string& class_id, const std::string& id,
125       const tizen_base::Bundle& b);
126   void ControlDestroy(const std::string& class_id, const std::string& id,
127       const tizen_base::Bundle& b);
128   void ControlResume(const std::string& class_id, const std::string& id,
129       const tizen_base::Bundle& b);
130   void ControlPause(const std::string& class_id, const std::string& id,
131       const tizen_base::Bundle& b);
132   void ControlChangePeriod(const std::string& class_id, const std::string& id,
133       const tizen_base::Bundle& b);
134   void ControlUpdate(const std::string& class_id, const std::string& id,
135       const tizen_base::Bundle& b);
136   void ControlResize(const std::string& class_id, const std::string& id,
137       const tizen_base::Bundle& b);
138   void GetContent(tizen_base::Bundle& b);
139 };
140
141 WidgetBase* __widget;
142
143 class WidgetContext::Impl {
144  public:
145   explicit Impl(WidgetContext* parent) : parent_(parent) {}
146
147   ~Impl() {
148     UnsetPeriodicTimer();
149   }
150
151  private:
152   void UpdateProcess(const tizen_base::Bundle& b);
153   void OnUpdate(bool force);
154   void SetPeriod(double period);
155   void SetPeriodicTimer();
156   void UnsetPeriodicTimer();
157   static gboolean TimedOutCb(gpointer user_data);
158
159  private:
160   friend class WidgetContext;
161   friend class WidgetBase;
162   friend class WidgetBase::Impl;
163
164   WidgetContext* parent_;
165
166   std::unique_ptr<tizen_base::Bundle> args_;
167   std::string content_;
168   double period_ = 0.0f;
169   guint periodic_timer_ = 0;
170   bool pending_update_ = false;
171   std::string pending_content_;
172 };
173
174 void WidgetBase::Impl::ControlCreate(const std::string& class_id,
175     const std::string& id, const tizen_base::Bundle& b) {
176   if (parent_->FindById(id).get() != nullptr) {
177     _E("Already exists. ID(%s)", id.c_str());
178     return;
179   }
180
181   std::shared_ptr<Context> context;
182   try {
183     context = parent_->CreateContext(class_id, id);
184   } catch (std::runtime_error& e) {
185     _E("runtime_error exception occurs");
186     return;
187   }
188
189   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
190   if (wc == nullptr)
191     return;
192
193   wc->impl_->args_.reset(new tizen_base::Bundle(b));
194   auto ctx = parent_->RunContext(context);
195   if (ctx.get() == nullptr)
196     return;
197
198   wc->impl_->args_.reset();
199   wc->impl_->content_ = b.GetString(WIDGET_K_CONTENT_INFO);
200 }
201
202 void WidgetBase::Impl::ControlDestroy(const std::string& class_id,
203     const std::string& id, const tizen_base::Bundle& b) {
204   auto context = parent_->FindById(id);
205   if (context.get() == nullptr) {
206     _E("Failed to find widget context. ID(%s)", id.c_str());
207     aul_widget_instance_del(class_id.c_str(), id.c_str());
208     return;
209   }
210
211   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
212   if (wc == nullptr)
213     return;
214
215   wc->impl_->args_.reset(new tizen_base::Bundle(b));
216   parent_->ExitContext(context);
217 }
218
219 void WidgetBase::Impl::ControlResume(const std::string& class_id,
220     const std::string& id, const tizen_base::Bundle& b) {
221   auto context = parent_->FindById(id);
222   if (context.get() == nullptr) {
223     _E("Failed to find widget context. ID(%s)", id.c_str());
224     return;
225   }
226
227   context->Resume();
228 }
229
230 void WidgetBase::Impl::ControlPause(const std::string& class_id,
231     const std::string& id, const tizen_base::Bundle& b) {
232   auto context = parent_->FindById(id);
233   if (context.get() == nullptr) {
234     _E("Failed to find widget context. ID(%s)", id.c_str());
235     return;
236   }
237
238   context->Pause();
239 }
240
241 void WidgetBase::Impl::ControlUpdate(const std::string& class_id,
242     const std::string& id, const tizen_base::Bundle& b) {
243   if (id.empty()) {
244     for (auto& i : parent_->GetContexts()) {
245       if (i->GetContextId() == class_id) {
246         WidgetContext* cxt = dynamic_cast<WidgetContext*>(i.get());
247         if (cxt == nullptr)
248           continue;
249
250         cxt->impl_->UpdateProcess(b);
251       }
252     }
253     return;
254   }
255
256   auto context = parent_->FindById(id);
257   if (context.get() == nullptr) {
258     _E("Failed to find widget context. ID(%s)", id.c_str());
259     return;
260   }
261
262   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
263   if (wc == nullptr)
264     return;
265
266   wc->impl_->UpdateProcess(b);
267 }
268
269 void WidgetBase::Impl::ControlResize(const std::string& class_id,
270     const std::string& id, const tizen_base::Bundle& b) {
271   auto context = parent_->FindById(id);
272   if (context.get() == nullptr) {
273     _E("Failed to find widget context. ID(%s)", id.c_str());
274     return;
275   }
276
277   int w = 0;
278   std::string w_str = b.GetString(WIDGET_K_WIDTH);
279   if (!w_str.empty()) {
280     char* remain = nullptr;
281     w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
282   }
283
284   int h = 0;
285   std::string h_str = b.GetString(WIDGET_K_HEIGHT);
286   if (!h_str.empty()) {
287     char* remain = nullptr;
288     h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
289   }
290
291   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
292   if (wc == nullptr)
293     return;
294
295   wc->OnResize(w, h);
296
297   _D("WidgetContext(%s) is resized to %dx%d", id.c_str(), w, h);
298   __context.SendUpdateStatus(class_id, id,
299       WIDGET_INSTANCE_EVENT_SIZE_CHANGED, 0, nullptr);
300 }
301
302 void WidgetBase::Impl::ControlChangePeriod(const std::string& class_id,
303     const std::string& id, const tizen_base::Bundle& b) {
304   auto context = parent_->FindById(id);
305   if (context.get() == nullptr) {
306     _E("Failed to find widget context. ID(%s)", id.c_str());
307     return;
308   }
309
310   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
311   if (wc == nullptr)
312     return;
313
314   wc->impl_->UnsetPeriodicTimer();
315
316   auto v = b.GetByte(WIDGET_K_PERIOD);
317   if (v.empty())
318     return;
319
320   const double* period = reinterpret_cast<double*>(v.data());
321   wc->impl_->SetPeriod(*period);
322   wc->impl_->SetPeriodicTimer();
323 }
324
325 void WidgetBase::Impl::GetContent(tizen_base::Bundle& b) {
326   std::string instance_id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
327   if (instance_id.empty()) {
328     _E("Instance ID is nullptr");
329     return;
330   }
331
332   auto context = parent_->FindById(instance_id);
333   if (context.get() == nullptr) {
334     _E("Failed to find widget context. ID(%s)", instance_id.c_str());
335     return;
336   }
337
338   WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
339   if (wc == nullptr)
340     return;
341
342   if (!wc->impl_->content_.empty()) {
343     b.Add(AUL_K_WIDGET_CONTENT_INFO, wc->impl_->content_);
344     _D("Content info of %s found", instance_id.c_str());
345   } else {
346     b.Add(AUL_K_WIDGET_CONTENT_INFO, "");
347     _D("Empty content info added");
348   }
349 }
350
351 WidgetBase::WidgetBase() : impl_(std::make_unique<WidgetBase::Impl>(this)) {
352 }
353
354 WidgetBase::~WidgetBase() = default;
355
356 std::string WidgetBase::GetViewerEndpoint() {
357   return __context.GetViewerEndpoint();
358 }
359
360 void WidgetBase::Run(int argc, char** argv) {
361   bundle* kb = bundle_import_from_argv(argc, argv);
362   if (kb) {
363     tizen_base::Bundle b(kb, false, true);
364     __context.SetViewerEndpoint(b.GetString(AUL_K_WIDGET_VIEWER));
365     if (!__context.GetViewerEndpoint().empty())
366       _D("Viewer endpoint :%s", __context.GetViewerEndpoint().c_str());
367     else
368       _E("Endpoint is missing");
369   } else {
370     _E("Failed to get launch argv");
371     throw std::runtime_error("Failed to get launch argv");
372   }
373
374   AppCoreMultiWindowBase::Run(argc, argv);
375 }
376
377 void WidgetBase::Dispose() {
378   AppCoreMultiWindowBase::Dispose();
379   if (getenv("AUL_LOADER_INIT")) {
380       unsetenv("AUL_LOADER_INIT");
381       OnLoopFinish();
382   }
383 }
384
385 void WidgetBase::ExitContext(
386     std::shared_ptr<AppCoreMultiWindowBase::Context> context) {
387   AppCoreMultiWindowBase::ExitContext(context);
388   int cnt = GetContextCnt();
389   if (cnt == 0)
390     Exit();
391
392   aul_widget_write_log(LOG_TAG,
393       "[%s:%d] instance_id(%s)", __FUNCTION__, __LINE__,
394       context->GetInstId().c_str());
395 }
396
397 void WidgetBase::Exit() {
398   AppCoreMultiWindowBase::Exit();
399   int cnt = GetContextCnt();
400   int ret = 0;
401   if (cnt == 0 && __context.IsPermanent())
402     ret = aul_notify_exit();
403
404   aul_widget_write_log(LOG_TAG,
405       "[%s:%d] exit : ret(%d), cnt(%d), permanent(%d)",
406       __FUNCTION__, __LINE__, ret, cnt, __context.IsPermanent());
407 }
408
409 int WidgetBase::OnCreate() {
410   AppCoreMultiWindowBase::OnCreate();
411
412   char appid[256] = { 0, };
413   int ret = aul_app_get_appid_bypid(getpid(), appid, sizeof(appid));
414   if (ret != AUL_R_OK) {
415     _E("aul_app_get_appid_bypid() is failed. error(%d)", ret);
416     return -1;
417   }
418
419   __context.SetAppId(appid);
420
421   char pkgid[256] = { 0, };
422   ret = aul_app_get_pkgid_bypid(getpid(), pkgid, sizeof(pkgid));
423   if (ret != AUL_R_OK) {
424     _E("aul_app_get_pkgid_bypid() is failed. error(%d)", ret);
425     return -1;
426   }
427
428   __context.SetPackageId(pkgid);
429
430   screen_connector_provider_init();
431   _D("Widget base is created");
432   __widget = this;
433   return 0;
434 }
435
436 int WidgetBase::OnTerminate() {
437   screen_connector_provider_fini();
438   __widget = nullptr;
439
440   AppCoreMultiWindowBase::OnTerminate();
441   _D("Widget base is terminated");
442   return 0;
443 }
444
445 int WidgetBase::OnReceive(aul_type type, tizen_base::Bundle b) {
446   AppCoreMultiWindowBase::OnReceive(type,
447       tizen_base::Bundle(b.GetHandle(), false, false));
448
449   switch (type) {
450     case AUL_RESUME: {
451         for (auto& i : GetContexts())
452           i->Resume();
453       }
454       break;
455     case AUL_TERMINATE:
456       Exit();
457       break;
458     case AUL_WIDGET_CONTENT:
459       impl_->GetContent(b);
460       break;
461     default:
462       break;
463   }
464
465   return 0;
466 }
467
468 int WidgetBase::OnControl(tizen_base::Bundle b) {
469   AppCoreMultiWindowBase::OnControl(
470       tizen_base::Bundle(b.GetHandle(), false, false));
471
472   std::string class_id = b.GetString(WIDGET_K_CLASS);
473   /* for previous version compatibility, use appid for default class id */
474   if (class_id.empty())
475     class_id = __context.GetAppId();
476
477   std::string id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
478   std::string operation = b.GetString(WIDGET_K_OPERATION);
479   if (operation.empty()) {
480     _E("Operation is empty");
481     return 0;
482   }
483
484   _I("Operation(%s)", operation.c_str());
485   if (operation == "create")
486     impl_->ControlCreate(class_id, id, b);
487   else if (operation == "resize")
488     impl_->ControlResize(class_id, id, b);
489   else if (operation == "update")
490     impl_->ControlUpdate(class_id, id, b);
491   else if (operation == "destroy")
492     impl_->ControlDestroy(class_id, id, b);
493   else if (operation == "resume")
494     impl_->ControlResume(class_id, id, b);
495   else if (operation == "pause")
496     impl_->ControlPause(class_id, id, b);
497   else if (operation == "terminate")
498     impl_->ControlDestroy(class_id, id, b);
499   else if (operation == "period")
500     impl_->ControlChangePeriod(class_id, id, b);
501
502   return 0;
503 }
504
505 /* LCOV_EXCL_START */
506 void WidgetContext::Impl::OnUpdate(bool force) {
507   parent_->OnUpdate(pending_content_.empty() ? tizen_base::Bundle() :
508       tizen_base::Bundle(pending_content_), force);
509
510   std::string class_id = parent_->GetContextId();
511   std::string id = parent_->GetInstId();
512   __context.SendUpdateStatus(class_id, id,
513       WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
514   _D("Updated: %s", id.c_str());
515 }
516 /* LCOV_EXCL_STOP */
517
518 void WidgetContext::Impl::UpdateProcess(const tizen_base::Bundle& b) {
519   bool force;
520   std::string force_str = b.GetString(WIDGET_K_FORCE);
521   if (force_str == "true")
522     force = true;
523   else
524     force = false;
525
526   std::string content_raw = b.GetString(WIDGET_K_CONTENT_INFO);
527   if (!parent_->IsResumed() && !force) {
528     pending_content_ = content_raw;
529     pending_update_ = true;
530   } else {
531     parent_->OnUpdate(content_raw.empty() ? tizen_base::Bundle() :
532         tizen_base::Bundle(content_raw), force);
533
534     std::string class_id = parent_->GetContextId();
535     std::string id = parent_->GetInstId();
536     __context.SendUpdateStatus(class_id, id,
537         WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
538     _D("Updated: %s", id.c_str());
539   }
540 }
541
542 /* LCOV_EXCL_START */
543 void WidgetContext::Impl::SetPeriod(double period) {
544   period_ = period;
545 }
546
547 void WidgetContext::Impl::SetPeriodicTimer() {
548   if (periodic_timer_)
549     return;
550
551   if (period_ > 0) {
552     _D("Restart timer!");
553     periodic_timer_ = g_timeout_add_seconds(period_,
554         TimedOutCb, parent_);
555   }
556 }
557 /* LCOV_EXCL_STOP */
558
559 void WidgetContext::Impl::UnsetPeriodicTimer() {
560   if (periodic_timer_) {
561     _D("Remove timer!");
562     g_source_remove(periodic_timer_);
563     periodic_timer_ = 0;
564   }
565 }
566
567 /* LCOV_EXCL_START */
568 gboolean WidgetContext::Impl::TimedOutCb(gpointer user_data) {
569   WidgetContext* wc = reinterpret_cast<WidgetContext*>(user_data);
570   if (wc->IsResumed()) {
571     _D("Periodic update!");
572     wc->OnUpdate(tizen_base::Bundle(), false);
573
574     __context.SendUpdateStatus(wc->GetContextId(), wc->GetInstId(),
575         WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
576     _D("Updated: %s", wc->GetInstId().c_str());
577   } else {
578     wc->impl_->pending_update_ = true;
579     wc->impl_->UnsetPeriodicTimer();
580   }
581
582   return G_SOURCE_CONTINUE;
583 }
584 /* LCOV_EXCL_STOP */
585
586 WidgetContext::WidgetContext(std::string context_id, std::string inst_id,
587     AppCoreMultiWindowBase* app)
588     : AppCoreMultiWindowBase::Context(
589           std::move(context_id), std::move(inst_id), app),
590       impl_(std::make_unique<WidgetContext::Impl>(this)) {
591 }
592
593 WidgetContext::~WidgetContext() = default;
594
595 /* LCOV_EXCL_START */
596 void WidgetContext::OnPause() {
597   std::string id = GetInstId();
598   _D("WidgetContext(%s) is paused", id.c_str());
599   std::string class_id = GetContextId();
600   __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
601       WIDGET_INSTANCE_EVENT_PAUSE, 0, nullptr);
602
603   if (__context.IsFgSignal()) {
604     _D("Send bg signal to resourceD");
605     aul_widget_instance_change_status(class_id.c_str(), kStatusBackground);
606     __context.SetFgSignal(false);
607   }
608 }
609
610 void WidgetContext::OnResume() {
611   if (impl_->pending_update_) {
612     _D("Pending update!");
613     impl_->pending_update_ = false;
614     impl_->OnUpdate(false);
615     impl_->SetPeriodicTimer();
616   }
617
618   std::string id = GetInstId();
619   _D("WidgetContext(%s) is resumed", id.c_str());
620   std::string class_id = GetContextId();
621   __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
622       WIDGET_INSTANCE_EVENT_RESUME, 0, nullptr);
623
624   if (!__context.IsFgSignal()) {
625     _D("Send fg signal to resourceD");
626     aul_widget_instance_change_status(class_id.c_str(), kStatusForeground);
627     __context.SetFgSignal(true);
628   }
629 }
630 /* LCOV_EXCL_STOP */
631
632 void WidgetContext::OnResize(int w, int h) {
633 }
634
635 void WidgetContext::OnUpdate(const tizen_base::Bundle& contents, bool force) {
636 }
637
638 void WidgetContext::ExitAsync() {
639   tizen_base::Bundle b;
640   b.Add(WIDGET_K_OPERATION, "terminate");
641
642   impl_->args_.reset(new tizen_base::Bundle(std::move(b)));
643   g_idle_add([](gpointer user_data) -> gboolean {
644     if (__widget == nullptr)
645       return G_SOURCE_REMOVE;
646
647     WidgetContext* wc = static_cast<WidgetContext*>(user_data);
648
649     std::string id = wc->GetInstId();
650     auto context = __widget->FindById(id);
651     if (context.get() == nullptr) {
652       _E("Failed to find context. ID(%s)", id.c_str());
653       return G_SOURCE_REMOVE;
654     }
655
656     __widget->ExitContext(context);
657     return G_SOURCE_REMOVE;
658   }, this);
659 }
660
661 int WidgetContext::SetContents(const tizen_base::Bundle& contents) {
662   std::string id = GetInstId();
663   if (id.empty())
664     return WIDGET_ERROR_FAULT;
665
666   std::string class_id = GetContextId();
667   if (class_id.empty())
668     return WIDGET_ERROR_FAULT;
669
670   int ret = __context.SendUpdateStatus(class_id, id,
671       WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, contents.GetHandle());
672   if (ret < 0) {
673     _E("Failed to send content info: %s of %s (%d)",
674         id.c_str(), class_id.c_str(), ret);
675     return WIDGET_ERROR_FAULT;
676   }
677
678   try {
679     auto contents_raw = const_cast<tizen_base::Bundle&>(contents).ToRaw();
680     impl_->content_ = std::string(
681         reinterpret_cast<char*>(contents_raw.first.get()));
682   } catch (const std::bad_alloc& e) {
683     impl_->content_ = "";
684     _E("Exception(%s) occurs", e.what());
685   }
686
687   return WIDGET_ERROR_NONE;
688 }
689
690 int WidgetContext::WindowBind(std::string id, Ecore_Wl2_Window* wl_win) {
691   wl_surface* surface = ecore_wl2_window_surface_get(wl_win);
692   if (surface == nullptr) {
693     _E("Failed to get surface, wl_win(%p)", wl_win);
694     return -1;
695   }
696
697   screen_connector_provider_remote_enable(id.c_str(), surface);
698   WindowBind(wl_win);
699   return 0;
700 }
701
702 void WidgetContext::OnCreate() {
703   auto& b = impl_->args_;
704   std::string class_id = b->GetString(WIDGET_K_CLASS);
705   /* For previous version compatibility, use appid for default class id */
706   if (class_id.empty())
707     class_id = __context.GetAppId();
708
709   std::string id = b->GetString(AUL_K_WIDGET_INSTANCE_ID);
710   std::string operation = b->GetString(WIDGET_K_OPERATION);
711   if (operation.empty()) {
712     _E("Operation is empty");
713     return;
714   }
715
716   std::string w_str = b->GetString(WIDGET_K_WIDTH);
717   int w = 0;
718   if (!w_str.empty()) {
719     char* remain = nullptr;
720     w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
721   }
722
723   std::string h_str = b->GetString(WIDGET_K_HEIGHT);
724   int h = 0;
725   if (!h_str.empty()) {
726     char* remain = nullptr;
727     h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
728   }
729
730   tizen_base::Bundle content_info;
731   std::string content = b->GetString(WIDGET_K_CONTENT_INFO);
732   try {
733     if (!content.empty())
734       content_info = tizen_base::Bundle(content);
735   } catch (std::bad_alloc& e) {
736     _W("Failed to decode content info(%s)", content.c_str());
737   }
738
739   bool r = OnCreate(content_info, w, h);
740   if (!r) {
741     _W("Create callback returns error");
742     int ret = __context.SendUpdateStatus(class_id, id,
743         WIDGET_INSTANCE_EVENT_CREATE_ABORTED, WIDGET_ERROR_CANCELED, nullptr);
744     if (ret != 0)
745       _E("Failed to send abrot status. error(%d)", ret);
746
747     throw std::runtime_error("Create callback returns error");
748   } else {
749     _D("WidgetContext(%s) is created", id.c_str());
750     aul_widget_instance_add(class_id.c_str(), id.c_str());
751     int ret = __context.SendUpdateStatus(class_id, id,
752         WIDGET_INSTANCE_EVENT_CREATE, 0, nullptr);
753     if (ret != 0)
754       _E("Failed to send create status. error(%d)", ret);
755
756     auto v = b->GetByte(WIDGET_K_PERIOD);
757     if (v.empty())
758       return;
759
760     const double* period = reinterpret_cast<double*>(v.data());
761     if (*period > 0) {
762       _I("Set periodic update timer. period(%lf)", *period);
763       impl_->SetPeriod(*period);
764       impl_->SetPeriodicTimer();
765     }
766   }
767 }
768
769 void WidgetContext::OnTerminate() {
770   DestroyType reason = DestroyType::TEMPORARY;
771   int event = WIDGET_INSTANCE_EVENT_TERMINATE;
772   std::string id = GetInstId();
773   std::string class_id = GetContextId();
774   auto& b = impl_->args_;
775   if (b.get()) {
776     std::string operation = b->GetString(WIDGET_K_OPERATION);
777     if (operation == "destroy")
778       reason = DestroyType::PERMANENT;
779   }
780
781   tizen_base::Bundle content_info;
782   try {
783     if (!impl_->content_.empty())
784       content_info = tizen_base::Bundle(impl_->content_);
785   } catch (const std::bad_alloc& e) {
786     _E("Exception(%s) occurs", e.what());
787   }
788
789   OnDestroy(reason, content_info);
790
791   _W("WidgetContext(%s) is destroyed. reason(%s)", id.c_str(),
792       reason == DestroyType::TEMPORARY ? "TEMPORARY" : "PERMANENT");
793   if (reason == DestroyType::PERMANENT) {
794     __context.SetPermanent(true);
795     event = WIDGET_INSTANCE_EVENT_DESTROY;
796     aul_widget_instance_del(class_id.c_str(), id.c_str());
797   } else {
798     __context.SetPermanent(false);
799     __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
800         WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, content_info.GetHandle());
801   }
802
803   impl_->UnsetPeriodicTimer();
804
805   __context.SendUpdateStatus(class_id.c_str(), id.c_str(), event, 0, nullptr);
806 }
807
808 }  // namespace tizen_cpp