+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aul.h>
+#include <aul_app_com.h>
+#include <aul_widget.h>
+#include <bundle_internal.h>
+#include <dlog.h>
+#include <glib.h>
+#include <screen_connector_provider.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <widget_errno.h>
+#include <widget_instance.h>
+
+#include <app_core_multi_window_base.hh>
+
+#include "common/log_private.hh"
+#include "include/widget_base.hh"
+
+namespace tizen_cpp {
+namespace {
+
+constexpr const char kStatusForeground[] = "fg";
+constexpr const char kStatusBackground[] = "bg";
+
+class AppContext {
+ public:
+ void SetAppId(const std::string& app_id) {
+ app_id_ = app_id;
+ }
+
+ const std::string& GetAppId() const {
+ return app_id_;
+ }
+
+ void SetPackageId(const std::string& package_id) {
+ package_id_ = package_id;
+ }
+
+ const std::string& GetPackageId() const {
+ return package_id_;
+ }
+
+ void SetViewerEndpoint(const std::string& viewer_endpoint) {
+ viewer_endpoint_ = viewer_endpoint;
+ }
+
+ const std::string& GetViewerEndpoint() const {
+ return viewer_endpoint_;
+ }
+
+ void SetPermanent(bool permanent) {
+ permanent_ = permanent;
+ }
+
+ bool IsPermanent() const {
+ return permanent_;
+ }
+
+ void SetFgSignal(bool fg_signal) {
+ fg_signal_ = fg_signal;
+ }
+
+ bool IsFgSignal() const {
+ return fg_signal_;
+ }
+
+ int SendUpdateStatus(const std::string& class_id,
+ const std::string& instance_id, int status, int err,
+ bundle* extra) {
+ int ret = aul_widget_send_status_to_viewer(class_id.c_str(),
+ instance_id.c_str(), viewer_endpoint_.c_str(), status, err, extra);
+ if (ret != AUL_R_OK)
+ return ret;
+
+ int lifecycle = widget_instance_convert_event_to_lifecycle_status(status);
+ if (lifecycle > -1) {
+ ret = aul_widget_send_status_to_service(class_id.c_str(),
+ instance_id.c_str(), package_id_.c_str(), lifecycle);
+ }
+
+ return ret;
+ }
+
+ private:
+ std::string app_id_;
+ std::string package_id_;
+ std::string viewer_endpoint_;
+ bool permanent_ = false;
+ bool fg_signal_ = false;
+};
+
+AppContext __context;
+
+} // namespace
+
+class WidgetBase::Impl {
+ public:
+ explicit Impl(WidgetBase* parent) : parent_(parent) {}
+
+ private:
+ friend class WidgetBase;
+ WidgetBase* parent_;
+
+ void ControlCreate(const std::string& class_id, const std::string& id,
+ const tizen_base::Bundle& b);
+ void ControlDestroy(const std::string& class_id, const std::string& id,
+ const tizen_base::Bundle& b);
+ void ControlResume(const std::string& class_id, const std::string& id,
+ const tizen_base::Bundle& b);
+ void ControlPause(const std::string& class_id, const std::string& id,
+ const tizen_base::Bundle& b);
+ void ControlChangePeriod(const std::string& class_id, const std::string& id,
+ const tizen_base::Bundle& b);
+ void ControlUpdate(const std::string& class_id, const std::string& id,
+ const tizen_base::Bundle& b);
+ void ControlResize(const std::string& class_id, const std::string& id,
+ const tizen_base::Bundle& b);
+ void GetContent(tizen_base::Bundle& b);
+};
+
+WidgetBase* __widget;
+
+class WidgetContext::Impl {
+ public:
+ explicit Impl(WidgetContext* parent) : parent_(parent) {}
+
+ ~Impl() {
+ UnsetPeriodicTimer();
+ }
+
+ private:
+ void UpdateProcess(const tizen_base::Bundle& b);
+ void OnUpdate(bool force);
+ void SetPeriod(double period);
+ void SetPeriodicTimer();
+ void UnsetPeriodicTimer();
+ static gboolean TimedOutCb(gpointer user_data);
+
+ private:
+ friend class WidgetContext;
+ friend class WidgetBase;
+ friend class WidgetBase::Impl;
+
+ WidgetContext* parent_;
+
+ std::unique_ptr<tizen_base::Bundle> args_;
+ std::string content_;
+ double period_;
+ guint periodic_timer_ = 0;
+ bool pending_update_;
+ std::string pending_content_;
+};
+
+void WidgetBase::Impl::ControlCreate(const std::string& class_id,
+ const std::string& id, const tizen_base::Bundle& b) {
+ if (parent_->FindById(id).get() != nullptr) {
+ _E("Already exists. ID(%s)", id.c_str());
+ return;
+ }
+
+ std::shared_ptr<Context> context;
+ try {
+ context = parent_->CreateContext(class_id, id);
+ } catch (std::runtime_error& e) {
+ _E("runtime_error exception occurs");
+ return;
+ }
+
+ WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
+ if (wc == nullptr)
+ return;
+
+ wc->impl_->args_.reset(new tizen_base::Bundle(b));
+ auto ctx = parent_->RunContext(context);
+ if (ctx.get() == nullptr)
+ return;
+
+ wc->impl_->args_.reset();
+ wc->impl_->content_ = b.GetString(WIDGET_K_CONTENT_INFO);
+}
+
+void WidgetBase::Impl::ControlDestroy(const std::string& class_id,
+ const std::string& id, const tizen_base::Bundle& b) {
+ auto context = parent_->FindById(id);
+ if (context.get() == nullptr) {
+ _E("Failed to find widget context. ID(%s)", id.c_str());
+ aul_widget_instance_del(class_id.c_str(), id.c_str());
+ return;
+ }
+
+ WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
+ if (wc == nullptr)
+ return;
+
+ wc->impl_->args_.reset(new tizen_base::Bundle(b));
+ parent_->ExitContext(context);
+}
+
+void WidgetBase::Impl::ControlResume(const std::string& class_id,
+ const std::string& id, const tizen_base::Bundle& b) {
+ auto context = parent_->FindById(id);
+ if (context.get() == nullptr) {
+ _E("Failed to find widget context. ID(%s)", id.c_str());
+ return;
+ }
+
+ context->Resume();
+}
+
+void WidgetBase::Impl::ControlPause(const std::string& class_id,
+ const std::string& id, const tizen_base::Bundle& b) {
+ auto context = parent_->FindById(id);
+ if (context.get() == nullptr) {
+ _E("Failed to find widget context. ID(%s)", id.c_str());
+ return;
+ }
+
+ context->Pause();
+}
+
+void WidgetBase::Impl::ControlUpdate(const std::string& class_id,
+ const std::string& id, const tizen_base::Bundle& b) {
+ if (id.empty()) {
+ for (auto& i : parent_->GetContexts()) {
+ if (i->GetContextId() == class_id) {
+ WidgetContext* cxt = dynamic_cast<WidgetContext*>(i.get());
+ if (cxt == nullptr)
+ continue;
+
+ cxt->impl_->UpdateProcess(b);
+ }
+ }
+ return;
+ }
+
+ auto context = parent_->FindById(id);
+ if (context.get() == nullptr) {
+ _E("Failed to find widget context. ID(%s)", id.c_str());
+ return;
+ }
+
+ WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
+ if (wc == nullptr)
+ return;
+
+ wc->impl_->UpdateProcess(b);
+}
+
+void WidgetBase::Impl::ControlResize(const std::string& class_id,
+ const std::string& id, const tizen_base::Bundle& b) {
+ auto context = parent_->FindById(id);
+ if (context.get() == nullptr) {
+ _E("Failed to find widget context. ID(%s)", id.c_str());
+ return;
+ }
+
+ int w = 0;
+ std::string w_str = b.GetString(WIDGET_K_WIDTH);
+ if (!w_str.empty()) {
+ char* remain = nullptr;
+ w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
+ }
+
+ int h = 0;
+ std::string h_str = b.GetString(WIDGET_K_HEIGHT);
+ if (!h_str.empty()) {
+ char* remain = nullptr;
+ h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
+ }
+
+ WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
+ if (wc == nullptr)
+ return;
+
+ wc->OnResize(w, h);
+
+ _D("WidgetContext(%s) is resized to %dx%d", id.c_str(), w, h);
+ __context.SendUpdateStatus(class_id, id,
+ WIDGET_INSTANCE_EVENT_SIZE_CHANGED, 0, nullptr);
+}
+
+void WidgetBase::Impl::ControlChangePeriod(const std::string& class_id,
+ const std::string& id, const tizen_base::Bundle& b) {
+ auto context = parent_->FindById(id);
+ if (context.get() == nullptr) {
+ _E("Failed to find widget context. ID(%s)", id.c_str());
+ return;
+ }
+
+ WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
+ if (wc == nullptr)
+ return;
+
+ wc->impl_->UnsetPeriodicTimer();
+
+ auto v = b.GetByte(WIDGET_K_PERIOD);
+ if (v.empty())
+ return;
+
+ const double* period = reinterpret_cast<double*>(v.data());
+ wc->impl_->SetPeriod(*period);
+ wc->impl_->SetPeriodicTimer();
+}
+
+void WidgetBase::Impl::GetContent(tizen_base::Bundle& b) {
+ std::string instance_id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
+ if (instance_id.empty()) {
+ _E("Instance ID is nullptr");
+ return;
+ }
+
+ auto context = parent_->FindById(instance_id);
+ if (context.get() == nullptr) {
+ _E("Failed to find widget context. ID(%s)", instance_id.c_str());
+ return;
+ }
+
+ WidgetContext* wc = dynamic_cast<WidgetContext*>(context.get());
+ if (wc == nullptr)
+ return;
+
+ if (!wc->impl_->content_.empty()) {
+ b.Add(AUL_K_WIDGET_CONTENT_INFO, wc->impl_->content_);
+ _D("Content info of %s found", instance_id.c_str());
+ } else {
+ b.Add(AUL_K_WIDGET_CONTENT_INFO, "");
+ _D("Empty content info added");
+ }
+}
+
+WidgetBase::WidgetBase() : impl_(std::make_unique<WidgetBase::Impl>(this)) {
+}
+
+WidgetBase::~WidgetBase() = default;
+
+std::string WidgetBase::GetViewerEndpoint() {
+ return __context.GetViewerEndpoint();
+}
+
+void WidgetBase::Run(int argc, char** argv) {
+ bundle* kb = bundle_import_from_argv(argc, argv);
+ if (kb) {
+ tizen_base::Bundle b(kb, false, true);
+ __context.SetViewerEndpoint(b.GetString(AUL_K_WIDGET_VIEWER));
+ if (!__context.GetViewerEndpoint().empty())
+ _D("Viewer endpoint :%s", __context.GetViewerEndpoint().c_str());
+ else
+ _E("Endpoint is missing");
+ } else {
+ _E("Failed to get launch argv");
+ throw std::runtime_error("Failed to get launch argv");
+ }
+
+ AppCoreMultiWindowBase::Run(argc, argv);
+}
+
+void WidgetBase::Dispose() {
+ AppCoreMultiWindowBase::Dispose();
+ if (getenv("AUL_LOADER_INIT")) {
+ unsetenv("AUL_LOADER_INIT");
+ OnLoopFinish();
+ }
+}
+
+void WidgetBase::ExitContext(
+ std::shared_ptr<AppCoreMultiWindowBase::Context> context) {
+ AppCoreMultiWindowBase::ExitContext(context);
+ int cnt = GetContextCnt();
+ if (cnt == 0)
+ Exit();
+
+ aul_widget_write_log(LOG_TAG,
+ "[%s:%d] instance_id(%s)", __FUNCTION__, __LINE__,
+ context->GetInstId().c_str());
+}
+
+void WidgetBase::Exit() {
+ AppCoreMultiWindowBase::Exit();
+ int cnt = GetContextCnt();
+ int ret = 0;
+ if (cnt == 0 && __context.IsPermanent())
+ ret = aul_notify_exit();
+
+ aul_widget_write_log(LOG_TAG,
+ "[%s:%d] exit : ret(%d), cnt(%d), permanent(%d)",
+ __FUNCTION__, __LINE__, ret, cnt, __context.IsPermanent());
+}
+
+int WidgetBase::OnCreate() {
+ AppCoreMultiWindowBase::OnCreate();
+
+ char appid[256] = { 0, };
+ int ret = aul_app_get_appid_bypid(getpid(), appid, sizeof(appid));
+ if (ret != AUL_R_OK) {
+ _E("aul_app_get_appid_bypid() is failed. error(%d)", ret);
+ return -1;
+ }
+
+ __context.SetAppId(appid);
+
+ char pkgid[256] = { 0, };
+ ret = aul_app_get_pkgid_bypid(getpid(), pkgid, sizeof(pkgid));
+ if (ret != AUL_R_OK) {
+ _E("aul_app_get_pkgid_bypid() is failed. error(%d)", ret);
+ return -1;
+ }
+
+ __context.SetPackageId(pkgid);
+
+ screen_connector_provider_init();
+ _D("Widget base is created");
+ __widget = this;
+ return 0;
+}
+
+int WidgetBase::OnTerminate() {
+ screen_connector_provider_fini();
+ __widget = nullptr;
+
+ AppCoreMultiWindowBase::OnTerminate();
+ _D("Widget base is terminated");
+ return 0;
+}
+
+int WidgetBase::OnReceive(aul_type type, tizen_base::Bundle b) {
+ AppCoreMultiWindowBase::OnReceive(type,
+ tizen_base::Bundle(b.GetHandle(), false, false));
+
+ switch (type) {
+ case AUL_RESUME: {
+ for (auto& i : GetContexts())
+ i->Resume();
+ }
+ break;
+ case AUL_TERMINATE:
+ Exit();
+ break;
+ case AUL_WIDGET_CONTENT:
+ impl_->GetContent(b);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int WidgetBase::OnControl(tizen_base::Bundle b) {
+ AppCoreMultiWindowBase::OnControl(
+ tizen_base::Bundle(b.GetHandle(), false, false));
+
+ std::string class_id = b.GetString(WIDGET_K_CLASS);
+ /* for previous version compatibility, use appid for default class id */
+ if (class_id.empty())
+ class_id = __context.GetAppId();
+
+ std::string id = b.GetString(AUL_K_WIDGET_INSTANCE_ID);
+ std::string operation = b.GetString(WIDGET_K_OPERATION);
+ if (operation.empty()) {
+ _E("Operation is empty");
+ return 0;
+ }
+
+ _I("Operation(%s)", operation.c_str());
+ if (operation == "create")
+ impl_->ControlCreate(class_id, id, b);
+ else if (operation == "resize")
+ impl_->ControlResize(class_id, id, b);
+ else if (operation == "update")
+ impl_->ControlUpdate(class_id, id, b);
+ else if (operation == "destroy")
+ impl_->ControlDestroy(class_id, id, b);
+ else if (operation == "resume")
+ impl_->ControlResume(class_id, id, b);
+ else if (operation == "pause")
+ impl_->ControlPause(class_id, id, b);
+ else if (operation == "terminate")
+ impl_->ControlDestroy(class_id, id, b);
+ else if (operation == "period")
+ impl_->ControlChangePeriod(class_id, id, b);
+
+ return 0;
+}
+
+void WidgetContext::Impl::OnUpdate(bool force) {
+ parent_->OnUpdate(pending_content_.empty() ? tizen_base::Bundle() :
+ tizen_base::Bundle(pending_content_), force);
+
+ std::string class_id = parent_->GetContextId();
+ std::string id = parent_->GetInstId();
+ __context.SendUpdateStatus(class_id, id,
+ WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
+ _D("Updated: %s", id.c_str());
+}
+
+void WidgetContext::Impl::UpdateProcess(const tizen_base::Bundle& b) {
+ bool force;
+ std::string force_str = b.GetString(WIDGET_K_FORCE);
+ if (force_str == "true")
+ force = true;
+ else
+ force = false;
+
+ std::string content_raw = b.GetString(WIDGET_K_CONTENT_INFO);
+ if (!parent_->IsResumed() && !force) {
+ pending_content_ = content_raw;
+ pending_update_ = true;
+ } else {
+ parent_->OnUpdate(content_raw.empty() ? tizen_base::Bundle() :
+ tizen_base::Bundle(content_raw), force);
+
+ std::string class_id = parent_->GetContextId();
+ std::string id = parent_->GetInstId();
+ __context.SendUpdateStatus(class_id, id,
+ WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
+ _D("Updated: %s", id.c_str());
+ }
+}
+
+void WidgetContext::Impl::SetPeriod(double period) {
+ period_ = period;
+}
+
+void WidgetContext::Impl::SetPeriodicTimer() {
+ if (periodic_timer_)
+ return;
+
+ if (period_ > 0) {
+ _D("Restart timer!");
+ periodic_timer_ = g_timeout_add_seconds(period_,
+ TimedOutCb, parent_);
+ }
+}
+
+void WidgetContext::Impl::UnsetPeriodicTimer() {
+ if (periodic_timer_) {
+ _D("Remove timer!");
+ g_source_remove(periodic_timer_);
+ periodic_timer_ = 0;
+ }
+}
+
+gboolean WidgetContext::Impl::TimedOutCb(gpointer user_data) {
+ WidgetContext* wc = reinterpret_cast<WidgetContext*>(user_data);
+ if (wc->IsResumed()) {
+ _D("Periodic update!");
+ wc->OnUpdate(tizen_base::Bundle(), false);
+
+ __context.SendUpdateStatus(wc->GetContextId(), wc->GetInstId(),
+ WIDGET_INSTANCE_EVENT_UPDATE, 0, nullptr);
+ _D("Updated: %s", wc->GetInstId().c_str());
+ } else {
+ wc->impl_->pending_update_ = true;
+ wc->impl_->UnsetPeriodicTimer();
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+WidgetContext::WidgetContext(std::string context_id, std::string inst_id,
+ AppCoreMultiWindowBase* app)
+ : AppCoreMultiWindowBase::Context(
+ std::move(context_id), std::move(inst_id), app),
+ impl_(std::make_unique<WidgetContext::Impl>(this)) {
+}
+
+WidgetContext::~WidgetContext() = default;
+
+void WidgetContext::OnPause() {
+ std::string id = GetInstId();
+ _D("WidgetContext(%s) is paused", id.c_str());
+ std::string class_id = GetContextId();
+ __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
+ WIDGET_INSTANCE_EVENT_PAUSE, 0, nullptr);
+
+ if (__context.IsFgSignal()) {
+ _D("Send bg signal to resourceD");
+ aul_widget_instance_change_status(class_id.c_str(), kStatusBackground);
+ __context.SetFgSignal(false);
+ }
+}
+
+void WidgetContext::OnResume() {
+ if (impl_->pending_update_) {
+ _D("Pending update!");
+ impl_->pending_update_ = false;
+ impl_->OnUpdate(false);
+ impl_->SetPeriodicTimer();
+ }
+
+ std::string id = GetInstId();
+ _D("WidgetContext(%s) is resumed", id.c_str());
+ std::string class_id = GetContextId();
+ __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
+ WIDGET_INSTANCE_EVENT_RESUME, 0, nullptr);
+
+ if (!__context.IsFgSignal()) {
+ _D("Send fg signal to resourceD");
+ aul_widget_instance_change_status(class_id.c_str(), kStatusForeground);
+ __context.SetFgSignal(true);
+ }
+}
+
+void WidgetContext::OnResize(int w, int h) {
+}
+
+void WidgetContext::OnUpdate(const tizen_base::Bundle& contents, bool force) {
+}
+
+void WidgetContext::ExitAsync() {
+ tizen_base::Bundle b;
+ b.Add(WIDGET_K_OPERATION, "terminate");
+
+ impl_->args_.reset(new tizen_base::Bundle(std::move(b)));
+ g_idle_add([](gpointer user_data) -> gboolean {
+ if (__widget == nullptr)
+ return G_SOURCE_REMOVE;
+
+ WidgetContext* wc = static_cast<WidgetContext*>(user_data);
+
+ std::string id = wc->GetInstId();
+ auto context = __widget->FindById(id);
+ if (context.get() == nullptr) {
+ _E("Failed to find context. ID(%s)", id.c_str());
+ return G_SOURCE_REMOVE;
+ }
+
+ __widget->ExitContext(context);
+ return G_SOURCE_REMOVE;
+ }, this);
+}
+
+int WidgetContext::SetContents(const tizen_base::Bundle& contents) {
+ std::string id = GetInstId();
+ if (id.empty())
+ return WIDGET_ERROR_FAULT;
+
+ std::string class_id = GetContextId();
+ if (class_id.empty())
+ return WIDGET_ERROR_FAULT;
+
+ int ret = __context.SendUpdateStatus(class_id, id,
+ WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, contents.GetHandle());
+ if (ret < 0) {
+ _E("Failed to send content info: %s of %s (%d)",
+ id.c_str(), class_id.c_str(), ret);
+ return WIDGET_ERROR_FAULT;
+ }
+
+ return WIDGET_ERROR_NONE;
+}
+
+int WidgetContext::WindowBind(std::string id, Ecore_Wl2_Window* wl_win) {
+ wl_surface* surface = ecore_wl2_window_surface_get(wl_win);
+ if (surface == nullptr) {
+ _E("Failed to get surface, wl_win(%p)", wl_win);
+ return -1;
+ }
+
+ screen_connector_provider_remote_enable(id.c_str(), surface);
+ WindowBind(wl_win);
+ return 0;
+}
+
+void WidgetContext::OnCreate() {
+ auto& b = impl_->args_;
+ std::string class_id = b->GetString(WIDGET_K_CLASS);
+ /* For previous version compatibility, use appid for default class id */
+ if (class_id.empty())
+ class_id = __context.GetAppId();
+
+ std::string id = b->GetString(AUL_K_WIDGET_INSTANCE_ID);
+ std::string operation = b->GetString(WIDGET_K_OPERATION);
+ if (operation.empty()) {
+ _E("Operation is empty");
+ return;
+ }
+
+ std::string w_str = b->GetString(WIDGET_K_WIDTH);
+ int w = 0;
+ if (!w_str.empty()) {
+ char* remain = nullptr;
+ w = static_cast<int>(g_ascii_strtoll(w_str.c_str(), &remain, 10));
+ }
+
+ std::string h_str = b->GetString(WIDGET_K_HEIGHT);
+ int h = 0;
+ if (!h_str.empty()) {
+ char* remain = nullptr;
+ h = static_cast<int>(g_ascii_strtoll(h_str.c_str(), &remain, 10));
+ }
+
+ tizen_base::Bundle content_info;
+ std::string content = b->GetString(WIDGET_K_CONTENT_INFO);
+ try {
+ if (!content.empty())
+ content_info = tizen_base::Bundle(content);
+ } catch (std::bad_alloc& e) {
+ _W("Failed to decode content info(%s)", content.c_str());
+ }
+
+ bool r = OnCreate(content_info, w, h);
+ if (!r) {
+ _W("Create callback returns error");
+ int ret = __context.SendUpdateStatus(class_id, id,
+ WIDGET_INSTANCE_EVENT_CREATE_ABORTED, WIDGET_ERROR_CANCELED, nullptr);
+ if (ret != 0)
+ _E("Failed to send abrot status. error(%d)", ret);
+
+ throw std::runtime_error("Create callback returns error");
+ } else {
+ _D("WidgetContext(%s) is created", id.c_str());
+ aul_widget_instance_add(class_id.c_str(), id.c_str());
+ int ret = __context.SendUpdateStatus(class_id, id,
+ WIDGET_INSTANCE_EVENT_CREATE, 0, nullptr);
+ if (ret != 0)
+ _E("Failed to send create status. error(%d)", ret);
+
+ auto v = b->GetByte(WIDGET_K_PERIOD);
+ if (v.empty())
+ return;
+
+ const double* period = reinterpret_cast<double*>(v.data());
+ if (*period > 0) {
+ _I("Set periodic update timer. period(%lf)", *period);
+ impl_->SetPeriod(*period);
+ impl_->SetPeriodicTimer();
+ }
+ }
+}
+
+void WidgetContext::OnTerminate() {
+ DestroyType reason = DestroyType::TEMPORARY;
+ int event = WIDGET_INSTANCE_EVENT_TERMINATE;
+ std::string id = GetInstId();
+ std::string class_id = GetContextId();
+ auto& b = impl_->args_;
+ if (b.get()) {
+ std::string operation = b->GetString(WIDGET_K_OPERATION);
+ if (operation == "destroy")
+ reason = DestroyType::PERMANENT;
+ }
+
+ tizen_base::Bundle content_info = impl_->content_.empty() ?
+ tizen_base::Bundle() : tizen_base::Bundle(impl_->content_);
+
+ OnDestroy(reason, content_info);
+
+ _W("WidgetContext(%s) is destroyed. reason(%s)", id.c_str(),
+ reason == DestroyType::TEMPORARY ? "TEMPORARY" : "PERMANENT");
+ if (reason == DestroyType::PERMANENT) {
+ __context.SetPermanent(true);
+ event = WIDGET_INSTANCE_EVENT_DESTROY;
+ aul_widget_instance_del(class_id.c_str(), id.c_str());
+ } else {
+ __context.SetPermanent(false);
+ __context.SendUpdateStatus(class_id.c_str(), id.c_str(),
+ WIDGET_INSTANCE_EVENT_EXTRA_UPDATED, 0, content_info.GetHandle());
+ }
+
+ impl_->UnsetPeriodicTimer();
+
+ __context.SendUpdateStatus(class_id.c_str(), id.c_str(), event, 0, nullptr);
+}
+
+} // namespace tizen_cpp