From d1f1fd614cc3e754e5731a728093684da555cfde Mon Sep 17 00:00:00 2001 From: "jh5.cho" Date: Thu, 14 Jan 2016 11:21:31 +0900 Subject: [PATCH] Add tizen-notification module Change-Id: I3ece1624f64f661d2020fe8ec0d0e90705e90c82 --- modules/CMakeLists.txt | 1 + modules/tizen-notification/CMakeLists.txt | 20 + .../tizen-notification/notification_extension.cc | 897 +++++++++++++++++ .../tizen-notification/notification_extension.h | 56 ++ modules/tizen-notification/package.json | 11 + modules/tizen-notification/picojson.h | 1037 ++++++++++++++++++++ .../tizen-notification/tizen-notification_api.js | 909 +++++++++++++++++ packaging/jsnative.spec | 1 + 8 files changed, 2932 insertions(+) create mode 100644 modules/tizen-notification/CMakeLists.txt create mode 100644 modules/tizen-notification/notification_extension.cc create mode 100644 modules/tizen-notification/notification_extension.h create mode 100644 modules/tizen-notification/package.json create mode 100644 modules/tizen-notification/picojson.h create mode 100644 modules/tizen-notification/tizen-notification_api.js diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index fa20bdc..e9989d6 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -12,3 +12,4 @@ ADD_SUBDIRECTORY(tizen-application-common) ADD_SUBDIRECTORY(tizen-app-preference) ADD_SUBDIRECTORY(tizen-sound-manager) +ADD_SUBDIRECTORY(tizen-notification) diff --git a/modules/tizen-notification/CMakeLists.txt b/modules/tizen-notification/CMakeLists.txt new file mode 100644 index 0000000..ee6c967 --- /dev/null +++ b/modules/tizen-notification/CMakeLists.txt @@ -0,0 +1,20 @@ +SET(MODULE "tizen-notification") + +# Native Module +ADD_MODULE(${MODULE} XWALK + JSAPI + tizen-notification_api.js + SRCS + notification_extension.cc + DEPENDS + dlog notification +) + +# Copy Project +INSTALL(FILES package.json + DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${MODULE} +) + +INSTALL(TARGETS ${MODULE} + DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${MODULE} +) \ No newline at end of file diff --git a/modules/tizen-notification/notification_extension.cc b/modules/tizen-notification/notification_extension.cc new file mode 100644 index 0000000..5e6c577 --- /dev/null +++ b/modules/tizen-notification/notification_extension.cc @@ -0,0 +1,897 @@ +/* + * Copyright (c) 2016 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 "notification_extension.h" + + +#include +#include +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "JSNative" + +namespace notification { + +/** + * @class NotificationExtension + */ + +/* @method NotificationExtension::CreateInstance() + * + * CreateInstance() SHOULD be implemented in inherited class + * to return your own ExtensionInstance class. + */ +xwalk::XWalkExtensionInstance* NotificationExtension::CreateInstance() { + return new NotificationInstance; +} + +/** + * @class NotificationInstance + */ + +/* @method NotificationInstance::HandleSyncMessage() + * + * HandleSyncMessage() CAN be implemented if want to handle syncronous messages + * sent by 'extension.internal.sendSyncMessage()' in echo_api.js. + * This method should send response with SendSyncReply(). + */ +void NotificationInstance::HandleSyncMessage(const char* msg) { + picojson::value value; + std::string err; + picojson::parse(value, msg, msg + strlen(msg), &err); + if (!err.empty()) { + LOGE("Ignoring message. Can't parse msessage : %s", err.c_str()); + return; + } + if (!value.is()) { + LOGE("Ignoring message. It is not an object."); + return; + } + auto& request = value.get(); + auto cmd = request["cmd"].to_str(); + + if (cmd == "post") { + LOGD("post command!"); + LOGD(" msg: %s", msg); + LOGD(" type: %s", request["type_"].to_str().c_str()); + PostOrUpdate(value, false); + } else if (cmd == "update") { + std::string type = request["type_"].to_str(); + PostOrUpdate(value, true); + } else if (cmd == "remove") { + int id = NOTIFICATION_PRIV_ID_NONE; + try { + id = std::stoi(request["id"].to_str()); + } catch(...) { + LOGE("convert notification id to integer failed"); + } + Remove(id); + } else if (cmd == "removeAll") { + RemoveAll(); + } else if (cmd == "getPkgName") { + int id = NOTIFICATION_PRIV_ID_NONE; + try { + id = std::stoi(request["id"].to_str()); + } catch(...) { + LOGE("convert notification id to integer failed"); + } + GetPkgName(id); + } else if (cmd == "getNotification") { + int id = NOTIFICATION_PRIV_ID_NONE; + try { + id = std::stoi(request["id"].to_str()); + } catch(...) { + LOGE("convert notification id to integer failed"); + } + GetNotification(request["asyncid"].to_str(), id); + } else if (cmd == "postStatusMessage") { + PostStatusMessage(request["asyncid"].to_str(), request["message"].to_str()); + } +} + +void NotificationInstance::AddButton(const notification_h& noti, + const picojson::value& appCtrl, + const notification_button_index_e + buttonIndex, + const notification_text_type_e + buttonTextType, + const std::string label, + const std::string imagePath) { + notification_add_button(noti, buttonIndex); + notification_set_text(noti, buttonTextType , + label.c_str(), NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + + notification_image_type_e imageType; + notification_event_type_e eventType; + if (buttonIndex == NOTIFICATION_BUTTON_1) { + imageType = NOTIFICATION_IMAGE_TYPE_BUTTON_1; + eventType = NOTIFICATION_EVENT_TYPE_CLICK_ON_BUTTON_1; + } else if (buttonIndex == NOTIFICATION_BUTTON_2) { + imageType = NOTIFICATION_IMAGE_TYPE_BUTTON_2; + eventType = NOTIFICATION_EVENT_TYPE_CLICK_ON_BUTTON_2; + } else if (buttonIndex == NOTIFICATION_BUTTON_3) { + imageType = NOTIFICATION_IMAGE_TYPE_BUTTON_3; + eventType = NOTIFICATION_EVENT_TYPE_CLICK_ON_BUTTON_3; + } + if (!imagePath.empty()) { + notification_set_image(noti, imageType, imagePath.c_str()); + } + + const char* appCtrlString = appCtrl.to_str().c_str(); + picojson::value v; + std::string err; + picojson::parse(v, appCtrlString, appCtrlString + strlen(appCtrlString), + &err); + if (!err.empty()) { + LOGE("Parsing the appControl error: %s", err.c_str()); + } + picojson::object appCtrlObj; + appCtrlObj = v.get(); + + app_control_h appControl; + app_control_create(&appControl); + const char* operation = appCtrlObj["__APP_SVC_OP_TYPE__"].to_str().c_str(); + LOGD("@ appControl operation set %s ", operation); + app_control_set_operation(appControl, operation); + auto found = appCtrlObj.find("__APP_SVC_URI__"); + if (found != appCtrlObj.end()) { + const char* uri = appCtrlObj["__APP_SVC_URI__"].to_str().c_str(); + LOGD("@ appControl uri set %s ", uri); + app_control_set_uri(appControl, uri); + } + found = appCtrlObj.find("__APP_SVC_MIME_TYPE__"); + if (found != appCtrlObj.end()) { + const char* mime = appCtrlObj["__APP_SVC_MIME_TYPE__"].to_str().c_str(); + LOGD("@ appControl mime set %s ", mime); + app_control_set_mime(appControl, mime); + } + found = appCtrlObj.find("__APP_SVC_CATEGORY__"); + if (found != appCtrlObj.end()) { + const char* category = appCtrlObj["__APP_SVC_CATEGORY__"].to_str().c_str(); + LOGD("@ appControl category set %s ", category); + app_control_set_category(appControl, category); + } + + int ret = notification_set_event_handler(noti, eventType, appControl); + if (ret != 0) { + LOGE("BUTTON, notification_set_event_handler() ERROR"); + return; + } +} + +void NotificationInstance::PostOrUpdate(const picojson::value& value, + bool isUpdate) { + LOGD("NotificationInstance::PostOrUpdate() execution"); + auto request = value.get(); + + int id = NOTIFICATION_PRIV_ID_NONE; + int ret = NOTIFICATION_ERROR_NONE; + notification_h noti = NULL; + std::string type = request["type_"].to_str(); + picojson::value dictionary = request["dictionary_"]; + try { + id = std::stoi(request["id_"].to_str()); + } catch (...) { + LOGE("convert notification id to integer failed"); + } + + if (isUpdate || (id > 0)) { + LOGD("notification_load, id : %d", id); + noti = notification_load(NULL, id); + if (noti == NULL) { + LOGE("Not found or removed notification id"); + return; + } + } else { + if (type == "type-noti") { + LOGD("Create notification, type: type-noti"); + noti = notification_create(NOTIFICATION_TYPE_NOTI); + } else if (type == "type-ongoing") { + LOGD("Create notification, type: type-ongoing"); + noti = notification_create(NOTIFICATION_TYPE_ONGOING); + } else if (type == "type-active") { + LOGD("Create notification, type: type-active"); + noti = notification_create(NOTIFICATION_TYPE_NOTI); + notification_set_display_applist(noti, NOTIFICATION_DISPLAY_APP_ACTIVE); + } + } + + if (dictionary.contains("iconPath")) { + ret = notification_set_image(noti, NOTIFICATION_IMAGE_TYPE_ICON, + dictionary.get("iconPath").to_str().c_str()); + LOGD("@ iconPath set %s", dictionary.get("iconPath").to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ iconPath set FAIL"); + } + } + + if (dictionary.contains("iconForIndicatorPath")) { + ret = notification_set_image(noti, + NOTIFICATION_IMAGE_TYPE_ICON_FOR_INDICATOR, + dictionary.get("iconForIndicatorPath") + .to_str().c_str()); + LOGD("@ iconForIndicatorPath set : %s", + dictionary.get("iconForIndicatorPath").to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ iconForIndicatorPath set FAIL"); + } + } + + if (dictionary.contains("iconForLockPath")) { + ret = notification_set_image(noti, NOTIFICATION_IMAGE_TYPE_ICON_FOR_LOCK, + dictionary.get("iconForLockPath").to_str() + .c_str()); + LOGD("@ iconForLockPath set : %s", dictionary.get("iconForLockPath") + .to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ iconForLockPathset FAIL"); + } + } + + if (dictionary.contains("thumbnailPath")) { + ret = notification_set_image(noti, NOTIFICATION_IMAGE_TYPE_THUMBNAIL, + dictionary.get("thumbnailPath").to_str() + .c_str()); + LOGD("@ thumbnailPath set : %s", dictionary.get("thumbnailPath") + .to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ thumbnailPath FAIL"); + } + } + + if (dictionary.contains("subIconPath")) { + ret = notification_set_image(noti, NOTIFICATION_IMAGE_TYPE_ICON_SUB , + dictionary.get("subIconPath").to_str() + .c_str()); + LOGD("@ subIconPath set : %s", dictionary.get("subIconPath").to_str() + .c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ subIconPath FAIL"); + } + } + + if (dictionary.contains("backgroundImagePath")) { + ret = notification_set_image(noti, NOTIFICATION_IMAGE_TYPE_BACKGROUND, + dictionary.get("backgroundImagePath") + .to_str().c_str()); + LOGD("@ backgroundImagePath set: %s", dictionary.get("backgroundImagePath") + .to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ backgroundImagePath FAIL"); + } + } + + if (dictionary.contains("title")) { + ret = notification_set_text(noti, NOTIFICATION_TEXT_TYPE_TITLE, + dictionary.get("title").to_str().c_str(), NULL, + NOTIFICATION_VARIABLE_TYPE_NONE); + LOGD("@ title set : %s", dictionary.get("title").to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ title set FAIL"); + } + } + + if (dictionary.contains("content")) { + ret = notification_set_text(noti, NOTIFICATION_TEXT_TYPE_CONTENT , + dictionary.get("content").to_str().c_str(), + NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + LOGD("@ content set : %s", dictionary.get("content").to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ content set FAIL"); + } + } + + if (dictionary.contains("timestamp")) { + LOGD("@ timestamp set %d ", dictionary.get("timestamp").to_str().c_str()); + ret = notification_set_time(noti, (time_t)dictionary.get("timestamp") + .get()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ timestamp set FAIL"); + } + } + + if (dictionary.contains("sound")) { + if (dictionary.get("sound").to_str().compare("none") == 0) { + ret = notification_set_sound(noti, NOTIFICATION_SOUND_TYPE_NONE, NULL); + } else if (dictionary.get("sound").to_str().compare("default") == 0) { + ret = notification_set_sound(noti, NOTIFICATION_SOUND_TYPE_DEFAULT, NULL); + } else { + ret = notification_set_sound(noti, NOTIFICATION_SOUND_TYPE_USER_DATA, + dictionary.get("sound").to_str().c_str()); + } + LOGD("@ sound set %s ", dictionary.get("sound").to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ sound set FAIL"); + } + } + + if (dictionary.contains("vibration")) { + if (dictionary.get("vibration").to_str().compare("none") == 0) { + ret = notification_set_vibration(noti, NOTIFICATION_VIBRATION_TYPE_NONE, + NULL); + } else if (dictionary.get("vibration").to_str().compare("default") == 0) { + ret = notification_set_vibration(noti, + NOTIFICATION_VIBRATION_TYPE_DEFAULT, + NULL); + } else { + ret = notification_set_vibration(noti, + NOTIFICATION_VIBRATION_TYPE_USER_DATA, + dictionary.get("vibration").to_str() + .c_str()); + } + LOGD("@ vibration set %s ", dictionary.get("vibration").to_str().c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ vibration set FAIL"); + } + } + + if (dictionary.contains("ledOption")) { + std::string led = dictionary.get("ledOption").to_str(); + if (led.compare("off") == 0) { + ret = notification_set_led(noti, NOTIFICATION_LED_OP_OFF, NULL); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ led set FAIL"); + } + } else if (led.compare("on") == 0) { + if (dictionary.contains("customColor")) { + ret = notification_set_led(noti, NOTIFICATION_LED_OP_ON_CUSTOM_COLOR, + static_cast(dictionary.get("customColor") + .get())); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ led set FAIL"); + } + LOGD("@ led custom color set %s ", static_cast(dictionary + .get("customColor").get())); + } else { + ret = notification_set_led(noti, NOTIFICATION_LED_OP_ON, NULL); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ led set FAIL"); + } + } + LOGD("@ led set %s ", dictionary.get("ledOption").to_str().c_str()); + if (dictionary.contains("ledOnPeriod") + && dictionary.contains("ledOffPeriod")) { + int ledOnPeriod = static_cast(dictionary.get("ledOnPeriod") + .get()); + int ledOffPeriod = static_cast(dictionary.get("ledOffPeriod") + .get()); + ret = notification_set_led_time_period(noti, ledOnPeriod, ledOffPeriod); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ led period set FAIL"); + } + LOGD("@ ledOnPeriod set %d ", static_cast(dictionary + .get("ledOnPeriod").get())); + LOGD("@ ledOffPeriod set %d ", static_cast(dictionary + .get("ledOffPeriod").get())); + } + } + } + + if (dictionary.contains("autoRemove")) { + bool autoRemove = true; + if (dictionary.get("autoRemove").to_str().compare("true") != 0) { + autoRemove = false; + } + ret = notification_set_auto_remove(noti, autoRemove); + LOGD("@ autoRemove set %d ", autoRemove); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ autoRemove set FAIL"); + } + } + + if (dictionary.contains("layout")) { + notification_ly_type_e layout; + if (dictionary.get("layout").to_str() == "none") { + layout = NOTIFICATION_LY_NONE; + } else if (dictionary.get("layout").to_str() == "event-single") { + layout = NOTIFICATION_LY_NOTI_EVENT_SINGLE; + } else if (dictionary.get("layout").to_str() == "event-multiple") { + layout = NOTIFICATION_LY_NOTI_EVENT_MULTIPLE; + } else if (dictionary.get("layout").to_str() == "thumbnail") { + layout = NOTIFICATION_LY_NOTI_THUMBNAIL; + } else if (dictionary.get("layout").to_str() == "ongoing-event") { + layout = NOTIFICATION_LY_ONGOING_EVENT; + } else if (dictionary.get("layout").to_str() == "ongoing-progress") { + layout = NOTIFICATION_LY_ONGOING_PROGRESS; + } + ret = notification_set_layout(noti, layout); + LOGD("@ layout set %d ", layout); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ layout set FAIL"); + } + } + + if (dictionary.contains("ActionAppControl")) { + picojson::value appCtrl = dictionary.get("ActionAppControl"); + if (appCtrl.to_str().compare("none") == 0) { + LOGD("@ no action is set"); + } else { + const char* appCtrlString = appCtrl.to_str().c_str(); + picojson::value v; + std::string err; + picojson::parse(v, appCtrlString, appCtrlString + strlen(appCtrlString) + , &err); + if (!err.empty()) { + LOGE("Parsing the appControl error: %s", err.c_str()); + } + picojson::object appCtrlObj; + appCtrlObj = v.get(); + + app_control_h appControl; + app_control_create(&appControl); + const char* operation = appCtrlObj["__APP_SVC_OP_TYPE__"].to_str() + .c_str(); + LOGD("@ appControl operation set %s ", operation); + app_control_set_operation(appControl, operation); + auto found = appCtrlObj.find("__APP_SVC_URI__"); + if (found != appCtrlObj.end()) { + const char* uri = appCtrlObj["__APP_SVC_URI__"].to_str().c_str(); + LOGD("@ appControl uri set %s ", uri); + app_control_set_uri(appControl, uri); + } + found = appCtrlObj.find("__APP_SVC_MIME_TYPE__"); + if (found != appCtrlObj.end()) { + const char* mime = appCtrlObj["__APP_SVC_MIME_TYPE__"].to_str().c_str(); + LOGD("@ appControl mime set %s ", mime); + app_control_set_mime(appControl, mime); + } + found = appCtrlObj.find("__APP_SVC_CATEGORY__"); + if (found != appCtrlObj.end()) { + const char* category = appCtrlObj["__APP_SVC_CATEGORY__"].to_str() + .c_str(); + LOGD("@ appControl category set %s ", category); + app_control_set_category(appControl, category); + } + if (dictionary.contains("ActionAppId")) { + app_control_set_app_id(appControl, dictionary.get("ActionAppId") + .to_str().c_str()); + } + + ret = notification_set_launch_option(noti, + NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, + static_cast(appControl)); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ appControl set FAIL"); + } + } + } else { + LOGD("No ActionAppControl, set default action"); + app_control_h appControl; + app_control_create(&appControl); + app_control_set_operation(appControl, APP_CONTROL_OPERATION_DEFAULT); + char* pkgname = NULL; + notification_get_pkgname(noti, &pkgname); + app_control_set_app_id(appControl, pkgname); + ret = notification_set_launch_option(noti, + NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, + static_cast(appControl)); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ default action set FAIL"); + } + } + + // can be deprecated + if (dictionary.contains("appControl")) { + picojson::value appCtrl = dictionary.get("appControl"); + const char* appCtrlString = appCtrl.to_str().c_str(); + picojson::value v; + std::string err; + picojson::parse(v, appCtrlString, appCtrlString + strlen(appCtrlString) + , &err); + if (!err.empty()) { + LOGE("Parsing the appControl error: %s", err.c_str()); + } + picojson::object appCtrlObj; + appCtrlObj = v.get(); + + app_control_h appControl; + app_control_create(&appControl); + const char* operation = appCtrlObj["__APP_SVC_OP_TYPE__"].to_str().c_str(); + LOGD("@ appControl operation set %s ", operation); + app_control_set_operation(appControl, operation); + auto found = appCtrlObj.find("__APP_SVC_URI__"); + if (found != appCtrlObj.end()) { + const char* uri = appCtrlObj["__APP_SVC_URI__"].to_str().c_str(); + LOGD("@ appControl uri set %s ", uri); + app_control_set_uri(appControl, uri); + } + found = appCtrlObj.find("__APP_SVC_MIME_TYPE__"); + if (found != appCtrlObj.end()) { + const char* mime = appCtrlObj["__APP_SVC_MIME_TYPE__"].to_str().c_str(); + LOGD("@ appControl mime set %s ", mime); + app_control_set_mime(appControl, mime); + } + found = appCtrlObj.find("__APP_SVC_CATEGORY__"); + if (found != appCtrlObj.end()) { + const char* category = appCtrlObj["__APP_SVC_CATEGORY__"].to_str() + .c_str(); + LOGD("@ appControl category set %s ", category); + app_control_set_category(appControl, category); + } + + ret = notification_set_launch_option(noti, + NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, + static_cast(appControl)); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ appControl set FAIL"); + } + } + + if (type == "type-ongoing") { + if (dictionary.contains("progressSize")) { + ret = notification_set_size(noti, dictionary.get("progressSize") + .get()); + LOGD("@ progressSize set %f ", dictionary.get("progressSize") + .get()); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ progressSize set FAIL"); + } + } + + if (dictionary.contains("progressPercentage")) { + ret = notification_set_progress(noti, dictionary.get("progressPercentage") + .get()/100.0); + LOGD("@ progressPercentage set %f ", dictionary.get("progressPercentage") + .get()/100.0); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGD("@ progressPercentage set FAIL"); + } + } + } + + if (type == "type-active") { + notification_button_index_e buttonIndex; + notification_text_type_e buttonTextType; + std::string imagePath; + std::string buttonLabel; + + if (dictionary.get("button1").to_str() == "ADD") { + buttonIndex = NOTIFICATION_BUTTON_1; + buttonTextType = NOTIFICATION_TEXT_TYPE_BUTTON_1; + if (dictionary.contains("button1ImagePath")) { + imagePath = dictionary.get("button1ImagePath").to_str(); + } + picojson::value appCtrl = dictionary.get("button1AppControl"); + buttonLabel = dictionary.get("button1Label").to_str(); + AddButton(noti, appCtrl, buttonIndex, buttonTextType, buttonLabel, + imagePath); + } + if (dictionary.get("button2").to_str() == "ADD") { + buttonIndex = NOTIFICATION_BUTTON_2; + buttonTextType = NOTIFICATION_TEXT_TYPE_BUTTON_2; + if (dictionary.contains("button2ImagePath")) { + imagePath = dictionary.get("button2ImagePath").to_str(); + } + picojson::value appCtrl = dictionary.get("button2AppControl"); + buttonLabel = dictionary.get("button2Label").to_str(); + AddButton(noti, appCtrl, buttonIndex, buttonTextType, buttonLabel, + imagePath); + } + if (dictionary.get("button3").to_str() == "ADD") { + buttonIndex = NOTIFICATION_BUTTON_3; + buttonTextType = NOTIFICATION_TEXT_TYPE_BUTTON_3; + if (dictionary.contains("button3ImagePath")) { + imagePath = dictionary.get("button3ImagePath").to_str(); + } + picojson::value appCtrl = dictionary.get("button3AppControl"); + buttonLabel = dictionary.get("button3Label").to_str(); + AddButton(noti, appCtrl, buttonIndex, buttonTextType, buttonLabel, + imagePath); + } + } + + picojson::value::object obj; + if (isUpdate) { + ret = notification_update(noti); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGE("Update notification Fail"); + obj["result"] = picojson::value("FAIL"); + obj["reason"] = picojson::value("Update notification Fail"); + SendSyncReply(picojson::value(obj).serialize().c_str()); + return; + } + obj["result"] = picojson::value("OK"); + } else { + ret = notification_insert(noti, &id); + if (ret != NOTIFICATION_ERROR_NONE) { + LOGE("Post notification Fail"); + obj["result"] = picojson::value("FAIL"); + if (ret == NOTIFICATION_ERROR_PERMISSION_DENIED) { + obj["reason"] = picojson::value("PERMISSION DENIED"); + } else { + obj["reason"] = picojson::value("Post notification Fail"); + } + SendSyncReply(picojson::value(obj).serialize().c_str()); + return; + } else { + LOGD("Post notification Success"); + obj["result"] = picojson::value("OK"); + obj["id"] = picojson::value(std::to_string(id)); + } + } + time_t insertionTime = 0; + notification_get_insert_time(noti, &insertionTime); + obj["insertionTime"] = picojson::value(static_cast(insertionTime) + * 1000.0); + SendSyncReply(picojson::value(obj).serialize().c_str()); +} + +void NotificationInstance::Remove(const int notificationId) { + picojson::value::object obj; + int ret = notification_delete_by_priv_id(NULL, NOTIFICATION_TYPE_NONE, + notificationId); + if (ret != NOTIFICATION_ERROR_NONE) { + obj["result"] = picojson::value("FAIL"); + obj["reason"] = picojson::value("Cannot remove notification error"); + } else { + obj["result"] = picojson::value("OK"); + } + SendSyncReply(picojson::value(obj).serialize().c_str()); +} + +void NotificationInstance::RemoveAll() { + picojson::value::object obj; + int ret = notification_delete_all(NOTIFICATION_TYPE_NOTI); + if (ret != NOTIFICATION_ERROR_NONE) { + obj["result"] = picojson::value("FAIL"); + obj["reason"] = picojson::value("RemoveAll(): TYPE_NOTI type failed"); + SendSyncReply(picojson::value(obj).serialize().c_str()); + return; + } + + ret = notification_delete_all(NOTIFICATION_TYPE_ONGOING); + if (ret != NOTIFICATION_ERROR_NONE) { + obj["result"] = picojson::value("FAIL"); + obj["reason"] = picojson::value("RemoveAll(): TYPE_ONGOING type failed"); + SendSyncReply(picojson::value(obj).serialize().c_str()); + return; + } + obj["result"] = picojson::value("OK"); + SendSyncReply(picojson::value(obj).serialize().c_str()); +} + +void NotificationInstance::GetNotificationHandle(const int id, + notification_h* noti_handle) { + *noti_handle = notification_load(NULL, id); + if (NULL == *noti_handle) { + LOGE("Not found or removed notification id"); + return; + } +} + +void NotificationInstance::GetPkgName(const int id) { + notification_h noti_handle = NULL; + GetNotificationHandle(id, ¬i_handle); + char* pkgname = NULL; + int ret = notification_get_pkgname(noti_handle, &pkgname); + picojson::value::object obj; + if (ret != NOTIFICATION_ERROR_NONE) { + obj["result"] = picojson::value("FAIL"); + obj["reason"] = picojson::value("notification_get_pkgname() Failed"); + } else { + obj["result"] = picojson::value("OK"); + obj["pkgname"] = picojson::value(pkgname); + } + SendSyncReply(picojson::value(obj).serialize().c_str()); +} + +void NotificationInstance::GetNotification(const std::string& async_id, + const int id) { + LOGD("@ Get Notification() start"); + picojson::value::object obj; + obj["asyncid"] = picojson::value(async_id); + notification_h noti_handle; + GetNotificationHandle(id, ¬i_handle); + + int applist; + notification_type_e notificationType; + notification_get_display_applist(noti_handle, &applist); + notification_get_type(noti_handle, ¬ificationType); + if (applist == NOTIFICATION_DISPLAY_APP_ACTIVE) { + obj["type"] = picojson::value("type-active"); + } else if (notificationType == NOTIFICATION_TYPE_ONGOING) { + obj["type"] = picojson::value("type-ongoing"); + } else if (notificationType == NOTIFICATION_TYPE_NOTI) { + obj["type"] = picojson::value("type-noti"); + } + + char* text; + notification_get_text(noti_handle, NOTIFICATION_TEXT_TYPE_TITLE, &text); + if (text != NULL) { + obj["title"] = picojson::value(text); + } + notification_get_text(noti_handle, NOTIFICATION_TEXT_TYPE_CONTENT, &text); + if (text != NULL) { + obj["content"] = picojson::value(text); + } + + char* imagePath; + notification_get_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON, &imagePath); + if (imagePath != NULL) { + obj["iconPath"] = picojson::value(imagePath); + } + notification_get_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON_FOR_INDICATOR + , &imagePath); + if (imagePath != NULL) { + obj["iconForIndicatorPath"] = picojson::value(imagePath); + } + notification_get_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON_FOR_LOCK, + &imagePath); + if (imagePath != NULL) { + obj["iconForLockPath"] = picojson::value(imagePath); + } + notification_get_image(noti_handle, NOTIFICATION_IMAGE_TYPE_THUMBNAIL, + &imagePath); + if (imagePath != NULL) { + obj["thumbnailPath"] = picojson::value(imagePath); + } + notification_get_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON_SUB, + &imagePath); + if (imagePath != NULL) { + obj["subIconPath"] = picojson::value(imagePath); + } + notification_get_image(noti_handle, NOTIFICATION_IMAGE_TYPE_BACKGROUND, + &imagePath); + if (imagePath != NULL) { + obj["backgroundImagePath"] = picojson::value(imagePath); + } + + notification_led_op_e operation = NOTIFICATION_LED_OP_OFF; + int ledArgb = -1; + notification_get_led(noti_handle, &operation, &ledArgb); + if (operation == NOTIFICATION_LED_OP_OFF) { + obj["ledOption"] = picojson::value("off"); + } else { + obj["ledOption"] = picojson::value("on"); + if (operation == NOTIFICATION_LED_OP_ON_CUSTOM_COLOR) { + obj["ledCustomColor"] = picojson::value(std::to_string(ledArgb)); + } + int onMs = -1; + int offMs = -1; + notification_get_led_time_period(noti_handle, &onMs, &offMs); + if (onMs != -1) { + obj["ledOnPeriod"] = picojson::value(std::to_string(onMs)); + obj["ledOffPeriod"] = picojson::value(std::to_string(offMs)); + } + } + + time_t timestamp; + notification_get_time(noti_handle, ×tamp); + if (timestamp != 0) { + obj["timestamp"] = picojson::value(static_cast(timestamp)); + } + + notification_sound_type_e soundType; + const char* soundPath; + notification_get_sound(noti_handle, &soundType, &soundPath); + if (soundType == NOTIFICATION_SOUND_TYPE_NONE) { + obj["sound"] = picojson::value("none"); + } else if (soundType == NOTIFICATION_SOUND_TYPE_DEFAULT) { + obj["sound"] = picojson::value("default"); + } else if (soundType == NOTIFICATION_SOUND_TYPE_USER_DATA) { + obj["sound"] = picojson::value(soundPath); + } + + notification_vibration_type_e vibrationType; + const char* vibrationPath; + notification_get_vibration(noti_handle, &vibrationType, &vibrationPath); + if (vibrationType == NOTIFICATION_VIBRATION_TYPE_NONE) { + obj["vibration"] = picojson::value("none"); + } else if (vibrationType == NOTIFICATION_VIBRATION_TYPE_DEFAULT) { + obj["vibration"] = picojson::value("default"); + } else if (vibrationType == NOTIFICATION_VIBRATION_TYPE_USER_DATA ) { + obj["vibration"] = picojson::value(vibrationPath); + } + + bool autoRemove = true; + notification_get_auto_remove(noti_handle, &autoRemove); + if (autoRemove == false) { + obj["autoRemove"] = picojson::value("false"); + } + + notification_ly_type_e layout = NOTIFICATION_LY_NONE; + notification_get_layout(noti_handle, &layout); + if (layout == NOTIFICATION_LY_NONE) { + obj["layout"] = picojson::value("none"); + } else if (layout == NOTIFICATION_LY_NOTI_EVENT_SINGLE) { + obj["layout"] = picojson::value("event-single"); + } else if (layout == NOTIFICATION_LY_NOTI_EVENT_MULTIPLE) { + obj["layout"] = picojson::value("event-multiple"); + } else if (layout == NOTIFICATION_LY_NOTI_THUMBNAIL) { + obj["layout"] = picojson::value("thumbnail"); + } else if (layout == NOTIFICATION_LY_ONGOING_EVENT) { + obj["layout"] = picojson::value("ongoing-event"); + } else if (layout == NOTIFICATION_LY_ONGOING_PROGRESS) { + obj["layout"] = picojson::value("ongoing-progress"); + } + + time_t insertionTime = 0; + notification_get_insert_time(noti_handle, &insertionTime); + obj["insertionTime"] = picojson::value(static_cast(insertionTime) + * 1000.0); + + if (notificationType == NOTIFICATION_TYPE_ONGOING) { + double size = -1.0; + notification_get_size(noti_handle, &size); + if (size >0) { + obj["progressSize"] = picojson::value(size); + } + double percentage = -1.0; + notification_get_progress(noti_handle, &percentage); + if (percentage >0) { + obj["progressPercentage"] = picojson::value(percentage); + } + } + + app_control_h app_control = NULL; + notification_get_launch_option(noti_handle, + NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, + static_cast(&app_control)); + if (app_control != NULL) { + char* operation; + app_control_get_operation(app_control, &operation); + obj["operation_"] = picojson::value(operation); + char* uri; + app_control_get_uri(app_control, &uri); + if (uri != NULL) { + obj["uri_"] = picojson::value(uri); + } + char* mime; + app_control_get_mime(app_control, &mime); + if (mime != NULL) { + obj["mime_"] = picojson::value(mime); + } + char* category; + app_control_get_category(app_control, &category); + if (category != NULL) { + obj["category_"] = picojson::value(category); + } + char* app_id; + app_control_get_app_id(app_control, &app_id); + if (app_id != NULL) { + obj["ActionAppId"] = picojson::value(app_id); + } + } + obj["result"] = picojson::value("OK"); + + SendSyncReply(picojson::value(obj).serialize().c_str()); +} + +void NotificationInstance::PostStatusMessage(const std::string& async_id, + const std::string& message) { + picojson::value::object obj; + obj["asyncid"] = picojson::value(async_id); + int ret = notification_status_message_post(message.c_str()); + if (ret != NOTIFICATION_ERROR_NONE) { + obj["result"] = picojson::value("FAIL"); + obj["reason"] = picojson::value("StatusMessagePost() Failed"); + } else { + obj["result"] = picojson::value("OK"); + } + SendSyncReply(picojson::value(obj).serialize().c_str()); +} + +} // namespace notification + +/* @macro EXPORT_XWALK_EXTENSION + * + * The implemented sub-class of XWalkExtension should be exported with + * EXPORT_XWALK_EXTENSION(name, class) macro. + */ +EXPORT_XWALK_EXTENSION(notification, notification::NotificationExtension); diff --git a/modules/tizen-notification/notification_extension.h b/modules/tizen-notification/notification_extension.h new file mode 100644 index 0000000..eb0d6a4 --- /dev/null +++ b/modules/tizen-notification/notification_extension.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016 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. + */ + +#ifndef NOTIFICATION_NOTIFICATION_EXTENSION_H_ +#define ECHO_ECHO_EXTENSION_H_ + +#include +#include + +#include "picojson.h" + +namespace notification { + +class NotificationExtension : public xwalk::XWalkExtension { + public: + // @override + xwalk::XWalkExtensionInstance* CreateInstance(); +}; + +class NotificationInstance : public xwalk::XWalkExtensionInstance { + public: + // @override + void HandleSyncMessage(const char* msg); + +private: + // sync + void PostOrUpdate(const picojson::value& value, bool isUpdate); + void Remove(const int notification_id); + void RemoveAll(); + void GetPkgName(const int id); + void GetNotification(const std::string& async_id, const int id); + void GetNotificationHandle(const int id, notification_h* noti_handle); + void AddButton(const notification_h& noti, const picojson::value& appCtrl, + const notification_button_index_e buttonIndex, + const notification_text_type_e buttonTextType, + const std::string label,const std::string imagePath); + void PostStatusMessage(const std::string& async_id, + const std::string& message); +}; + +} // namespace notification + +#endif // NOTIFICATION_NOTIFICATION_EXTENSION_H_ diff --git a/modules/tizen-notification/package.json b/modules/tizen-notification/package.json new file mode 100644 index 0000000..af8b534 --- /dev/null +++ b/modules/tizen-notification/package.json @@ -0,0 +1,11 @@ +{ + "name": "tizen-notification", + "version": "0.0.1", + "description": "Module for Tizen Notification", + "main": "tizen-notification.xwalk", + "author": { + "name": "Joonghyun Cho", + "email": "jh5.cho@samsung.com" + } +} + diff --git a/modules/tizen-notification/picojson.h b/modules/tizen-notification/picojson.h new file mode 100644 index 0000000..0ae6851 --- /dev/null +++ b/modules/tizen-notification/picojson.h @@ -0,0 +1,1037 @@ +/* + * Copyright 2009-2010 Cybozu Labs, Inc. + * Copyright 2011 Kazuho Oku + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CYBOZU LABS, INC. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL CYBOZU LABS, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Cybozu Labs, Inc. + * + */ +#ifndef picojson_h +#define picojson_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + #define SNPRINTF _snprintf_s + #pragma warning(push) + #pragma warning(disable : 4244) // conversion from int to char +#else + #define SNPRINTF snprintf +#endif + +namespace picojson { + + enum { + null_type, + boolean_type, + number_type, + string_type, + array_type, + object_type + }; + + struct null {}; + + class value { + public: + typedef std::vector array; + typedef std::map object; + union _storage { + bool boolean_; + double number_; + std::string* string_; + array* array_; + object* object_; + }; + protected: + int type_; + _storage u_; + public: + value(); + value(int type, bool); + explicit value(bool b); + explicit value(double n); + explicit value(const std::string& s); + explicit value(const array& a); + explicit value(const object& o); + explicit value(const char* s); + value(const char* s, size_t len); + ~value(); + value(const value& x); + value& operator=(const value& x); + void swap(value& x); + template bool is() const; + template const T& get() const; + template T& get(); + bool evaluate_as_boolean() const; + const value& get(size_t idx) const; + const value& get(const std::string& key) const; + bool contains(size_t idx) const; + bool contains(const std::string& key) const; + std::string to_str() const; + template void serialize(Iter os) const; + std::string serialize() const; + private: + template value(const T*); // intentionally defined to block implicit conversion of pointer to bool + }; + + typedef value::array array; + typedef value::object object; + + inline value::value() : type_(null_type) {} + + inline value::value(int type, bool) : type_(type) { + switch (type) { +#define INIT(p, v) case p##type: u_.p = v; break + INIT(boolean_, false); + INIT(number_, 0.0); + INIT(string_, new std::string()); + INIT(array_, new array()); + INIT(object_, new object()); +#undef INIT + default: break; + } + } + + inline value::value(bool b) : type_(boolean_type) { + u_.boolean_ = b; + } + + inline value::value(double n) : type_(number_type) { + u_.number_ = n; + } + + inline value::value(const std::string& s) : type_(string_type) { + u_.string_ = new std::string(s); + } + + inline value::value(const array& a) : type_(array_type) { + u_.array_ = new array(a); + } + + inline value::value(const object& o) : type_(object_type) { + u_.object_ = new object(o); + } + + inline value::value(const char* s) : type_(string_type) { + u_.string_ = new std::string(s); + } + + inline value::value(const char* s, size_t len) : type_(string_type) { + u_.string_ = new std::string(s, len); + } + + inline value::~value() { + switch (type_) { +#define DEINIT(p) case p##type: delete u_.p; break + DEINIT(string_); + DEINIT(array_); + DEINIT(object_); +#undef DEINIT + default: break; + } + } + + inline value::value(const value& x) : type_(x.type_) { + switch (type_) { +#define INIT(p, v) case p##type: u_.p = v; break + INIT(string_, new std::string(*x.u_.string_)); + INIT(array_, new array(*x.u_.array_)); + INIT(object_, new object(*x.u_.object_)); +#undef INIT + default: + u_ = x.u_; + break; + } + } + + inline value& value::operator=(const value& x) { + if (this != &x) { + this->~value(); + new (this) value(x); + } + return *this; + } + + inline void value::swap(value& x) { + std::swap(type_, x.type_); + std::swap(u_, x.u_); + } + +#define IS(ctype, jtype) \ + template <> inline bool value::is() const { \ + return type_ == jtype##_type; \ + } + IS(null, null) + IS(bool, boolean) + IS(int, number) + IS(double, number) + IS(std::string, string) + IS(array, array) + IS(object, object) +#undef IS + +#define GET(ctype, var) \ + template <> inline const ctype& value::get() const { \ + assert("type mismatch! call vis() before get()" \ + && is()); \ + return var; \ + } \ + template <> inline ctype& value::get() { \ + assert("type mismatch! call is() before get()" \ + && is()); \ + return var; \ + } + GET(bool, u_.boolean_) + GET(double, u_.number_) + GET(std::string, *u_.string_) + GET(array, *u_.array_) + GET(object, *u_.object_) +#undef GET + + inline bool value::evaluate_as_boolean() const { + switch (type_) { + case null_type: + return false; + case boolean_type: + return u_.boolean_; + case number_type: + return u_.number_ != 0; + case string_type: + return ! u_.string_->empty(); + default: + return true; + } + } + + inline const value& value::get(size_t idx) const { + static value s_null; + assert(is()); + return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; + } + + inline const value& value::get(const std::string& key) const { + static value s_null; + assert(is()); + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end() ? i->second : s_null; + } + + inline bool value::contains(size_t idx) const { + assert(is()); + return idx < u_.array_->size(); + } + + inline bool value::contains(const std::string& key) const { + assert(is()); + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end(); + } + + inline std::string value::to_str() const { + switch (type_) { + case null_type: return "null"; + case boolean_type: return u_.boolean_ ? "true" : "false"; + case number_type: { + char buf[256]; + double tmp; + SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); + return buf; + } + case string_type: return *u_.string_; + case array_type: return "array"; + case object_type: return "object"; + default: assert(0); +#ifdef _MSC_VER + __assume(0); +#endif + } + return std::string(); + } + + template void copy(const std::string& s, Iter oi) { + std::copy(s.begin(), s.end(), oi); + } + + template void serialize_str(const std::string& s, Iter oi) { + *oi++ = '"'; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { + switch (*i) { +#define MAP(val, sym) case val: copy(sym, oi); break + MAP('"', "\\\""); + MAP('\\', "\\\\"); + MAP('/', "\\/"); + MAP('\b', "\\b"); + MAP('\f', "\\f"); + MAP('\n', "\\n"); + MAP('\r', "\\r"); + MAP('\t', "\\t"); +#undef MAP + default: + if ((unsigned char)*i < 0x20 || *i == 0x7f) { + char buf[7]; + SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff); + copy(buf, buf + 6, oi); + } else { + *oi++ = *i; + } + break; + } + } + *oi++ = '"'; + } + + template void value::serialize(Iter oi) const { + switch (type_) { + case string_type: + serialize_str(*u_.string_, oi); + break; + case array_type: { + *oi++ = '['; + for (array::const_iterator i = u_.array_->begin(); + i != u_.array_->end(); + ++i) { + if (i != u_.array_->begin()) { + *oi++ = ','; + } + i->serialize(oi); + } + *oi++ = ']'; + break; + } + case object_type: { + *oi++ = '{'; + for (object::const_iterator i = u_.object_->begin(); + i != u_.object_->end(); + ++i) { + if (i != u_.object_->begin()) { + *oi++ = ','; + } + serialize_str(i->first, oi); + *oi++ = ':'; + i->second.serialize(oi); + } + *oi++ = '}'; + break; + } + default: + copy(to_str(), oi); + break; + } + } + + inline std::string value::serialize() const { + std::string s; + serialize(std::back_inserter(s)); + return s; + } + + template class input { + protected: + Iter cur_, end_; + int last_ch_; + bool ungot_; + int line_; + public: + input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {} + int getc() { + if (ungot_) { + ungot_ = false; + return last_ch_; + } + if (cur_ == end_) { + last_ch_ = -1; + return -1; + } + if (last_ch_ == '\n') { + line_++; + } + last_ch_ = *cur_++ & 0xff; + return last_ch_; + } + void ungetc() { + if (last_ch_ != -1) { + assert(! ungot_); + ungot_ = true; + } + } + Iter cur() const { return cur_; } + int line() const { return line_; } + void skip_ws() { + while (1) { + int ch = getc(); + if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { + ungetc(); + break; + } + } + } + bool expect(int expect) { + skip_ws(); + if (getc() != expect) { + ungetc(); + return false; + } + return true; + } + bool match(const std::string& pattern) { + for (std::string::const_iterator pi(pattern.begin()); + pi != pattern.end(); + ++pi) { + if (getc() != *pi) { + ungetc(); + return false; + } + } + return true; + } + }; + + template inline int _parse_quadhex(input &in) { + int uni_ch = 0, hex; + for (int i = 0; i < 4; i++) { + if ((hex = in.getc()) == -1) { + return -1; + } + if ('0' <= hex && hex <= '9') { + hex -= '0'; + } else if ('A' <= hex && hex <= 'F') { + hex -= 'A' - 0xa; + } else if ('a' <= hex && hex <= 'f') { + hex -= 'a' - 0xa; + } else { + in.ungetc(); + return -1; + } + uni_ch = uni_ch * 16 + hex; + } + return uni_ch; + } + + template inline bool _parse_codepoint(String& out, input& in) { + int uni_ch; + if ((uni_ch = _parse_quadhex(in)) == -1) { + return false; + } + if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { + if (0xdc00 <= uni_ch) { + // a second 16-bit of a surrogate pair appeared + return false; + } + // first 16-bit of surrogate pair, get the next one + if (in.getc() != '\\' || in.getc() != 'u') { + in.ungetc(); + return false; + } + int second = _parse_quadhex(in); + if (! (0xdc00 <= second && second <= 0xdfff)) { + return false; + } + uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); + uni_ch += 0x10000; + } + if (uni_ch < 0x80) { + out.push_back(uni_ch); + } else { + if (uni_ch < 0x800) { + out.push_back(0xc0 | (uni_ch >> 6)); + } else { + if (uni_ch < 0x10000) { + out.push_back(0xe0 | (uni_ch >> 12)); + } else { + out.push_back(0xf0 | (uni_ch >> 18)); + out.push_back(0x80 | ((uni_ch >> 12) & 0x3f)); + } + out.push_back(0x80 | ((uni_ch >> 6) & 0x3f)); + } + out.push_back(0x80 | (uni_ch & 0x3f)); + } + return true; + } + + template inline bool _parse_string(String& out, input& in) { + while (1) { + int ch = in.getc(); + if (ch < ' ') { + in.ungetc(); + return false; + } else if (ch == '"') { + return true; + } else if (ch == '\\') { + if ((ch = in.getc()) == -1) { + return false; + } + switch (ch) { +#define MAP(sym, val) case sym: out.push_back(val); break + MAP('"', '\"'); + MAP('\\', '\\'); + MAP('/', '/'); + MAP('b', '\b'); + MAP('f', '\f'); + MAP('n', '\n'); + MAP('r', '\r'); + MAP('t', '\t'); +#undef MAP + case 'u': + if (! _parse_codepoint(out, in)) { + return false; + } + break; + default: + return false; + } + } else { + out.push_back(ch); + } + } + return false; + } + + template inline bool _parse_array(Context& ctx, input& in) { + if (! ctx.parse_array_start()) { + return false; + } + if (in.expect(']')) { + return true; + } + size_t idx = 0; + do { + if (! ctx.parse_array_item(in, idx)) { + return false; + } + idx++; + } while (in.expect(',')); + return in.expect(']'); + } + + template inline bool _parse_object(Context& ctx, input& in) { + if (! ctx.parse_object_start()) { + return false; + } + if (in.expect('}')) { + return true; + } + do { + std::string key; + if (! in.expect('"') + || ! _parse_string(key, in) + || ! in.expect(':')) { + return false; + } + if (! ctx.parse_object_item(in, key)) { + return false; + } + } while (in.expect(',')); + return in.expect('}'); + } + + template inline bool _parse_number(double& out, input& in) { + std::string num_str; + while (1) { + int ch = in.getc(); + if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == '.' + || ch == 'e' || ch == 'E') { + num_str.push_back(ch); + } else { + in.ungetc(); + break; + } + } + char* endp; + out = strtod(num_str.c_str(), &endp); + return endp == num_str.c_str() + num_str.size(); + } + + template inline bool _parse(Context& ctx, input& in) { + in.skip_ws(); + int ch = in.getc(); + switch (ch) { +#define IS(ch, text, op) case ch: \ + if (in.match(text) && op) { \ + return true; \ + } else { \ + return false; \ + } + IS('n', "ull", ctx.set_null()); + IS('f', "alse", ctx.set_bool(false)); + IS('t', "rue", ctx.set_bool(true)); +#undef IS + case '"': + return ctx.parse_string(in); + case '[': + return _parse_array(ctx, in); + case '{': + return _parse_object(ctx, in); + default: + if (('0' <= ch && ch <= '9') || ch == '-') { + in.ungetc(); + double f; + if (_parse_number(f, in)) { + ctx.set_number(f); + return true; + } else { + return false; + } + } + break; + } + in.ungetc(); + return false; + } + + class deny_parse_context { + public: + bool set_null() { return false; } + bool set_bool(bool) { return false; } + bool set_number(double) { return false; } + template bool parse_string(input&) { return false; } + bool parse_array_start() { return false; } + template bool parse_array_item(input&, size_t) { + return false; + } + bool parse_object_start() { return false; } + template bool parse_object_item(input&, const std::string&) { + return false; + } + }; + + class default_parse_context { + protected: + value* out_; + public: + default_parse_context(value* out) : out_(out) {} + bool set_null() { + *out_ = value(); + return true; + } + bool set_bool(bool b) { + *out_ = value(b); + return true; + } + bool set_number(double f) { + *out_ = value(f); + return true; + } + template bool parse_string(input& in) { + *out_ = value(string_type, false); + return _parse_string(out_->get(), in); + } + bool parse_array_start() { + *out_ = value(array_type, false); + return true; + } + template bool parse_array_item(input& in, size_t) { + array& a = out_->get(); + a.push_back(value()); + default_parse_context ctx(&a.back()); + return _parse(ctx, in); + } + bool parse_object_start() { + *out_ = value(object_type, false); + return true; + } + template bool parse_object_item(input& in, const std::string& key) { + object& o = out_->get(); + default_parse_context ctx(&o[key]); + return _parse(ctx, in); + } + private: + default_parse_context(const default_parse_context&); + default_parse_context& operator=(const default_parse_context&); + }; + + class null_parse_context { + public: + struct dummy_str { + void push_back(int) {} + }; + public: + null_parse_context() {} + bool set_null() { return true; } + bool set_bool(bool) { return true; } + bool set_number(double) { return true; } + template bool parse_string(input& in) { + dummy_str s; + return _parse_string(s, in); + } + bool parse_array_start() { return true; } + template bool parse_array_item(input& in, size_t) { + return _parse(*this, in); + } + bool parse_object_start() { return true; } + template bool parse_object_item(input& in, const std::string&) { + return _parse(*this, in); + } + private: + null_parse_context(const null_parse_context&); + null_parse_context& operator=(const null_parse_context&); + }; + + // obsolete, use the version below + template inline std::string parse(value& out, Iter& pos, const Iter& last) { + std::string err; + pos = parse(out, pos, last, &err); + return err; + } + + template inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) { + input in(first, last); + if (! _parse(ctx, in) && err != NULL) { + char buf[64]; + SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); + *err = buf; + while (1) { + int ch = in.getc(); + if (ch == -1 || ch == '\n') { + break; + } else if (ch >= ' ') { + err->push_back(ch); + } + } + } + return in.cur(); + } + + template inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) { + default_parse_context ctx(&out); + return _parse(ctx, first, last, err); + } + + inline std::string parse(value& out, std::istream& is) { + std::string err; + parse(out, std::istreambuf_iterator(is.rdbuf()), + std::istreambuf_iterator(), &err); + return err; + } + + template struct last_error_t { + static std::string s; + }; + template std::string last_error_t::s; + + inline void set_last_error(const std::string& s) { + last_error_t::s = s; + } + + inline const std::string& get_last_error() { + return last_error_t::s; + } + + inline bool operator==(const value& x, const value& y) { + if (x.is()) + return y.is(); +#define PICOJSON_CMP(type) \ + if (x.is()) \ + return y.is() && x.get() == y.get() + PICOJSON_CMP(bool); + PICOJSON_CMP(double); + PICOJSON_CMP(std::string); + PICOJSON_CMP(array); + PICOJSON_CMP(object); +#undef PICOJSON_CMP + assert(0); +#ifdef _MSC_VER + __assume(0); +#endif + return false; + } + + inline bool operator!=(const value& x, const value& y) { + return ! (x == y); + } +} + +namespace std { + template<> inline void swap(picojson::value& x, picojson::value& y) + { + x.swap(y); + } +} + +inline std::istream& operator>>(std::istream& is, picojson::value& x) +{ + picojson::set_last_error(std::string()); + std::string err = picojson::parse(x, is); + if (! err.empty()) { + picojson::set_last_error(err); + is.setstate(std::ios::failbit); + } + return is; +} + +inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) +{ + x.serialize(std::ostream_iterator(os)); + return os; +} +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif +#ifdef TEST_PICOJSON +#ifdef _MSC_VER + #pragma warning(disable : 4127) // conditional expression is constant +#endif + +using namespace std; + +static void plan(int num) +{ + printf("1..%d\n", num); +} + +static bool success = true; + +static void ok(bool b, const char* name = "") +{ + static int n = 1; + if (! b) + success = false; + printf("%s %d - %s\n", b ? "ok" : "ng", n++, name); +} + +template void is(const T& x, const T& y, const char* name = "") +{ + if (x == y) { + ok(true, name); + } else { + ok(false, name); + } +} + +#include +#include +#include +#include + +int main(void) +{ + plan(85); + + // constructors +#define TEST(expr, expected) \ + is(picojson::value expr .serialize(), string(expected), "picojson::value" #expr) + + TEST( (true), "true"); + TEST( (false), "false"); + TEST( (42.0), "42"); + TEST( (string("hello")), "\"hello\""); + TEST( ("hello"), "\"hello\""); + TEST( ("hello", 4), "\"hell\""); + + { + double a = 1; + for (int i = 0; i < 1024; i++) { + picojson::value vi(a); + std::stringstream ss; + ss << vi; + picojson::value vo; + ss >> vo; + double b = vo.get(); + if ((i < 53 && a != b) || fabs(a - b) / b > 1e-8) { + printf("ng i=%d a=%.18e b=%.18e\n", i, a, b); + } + a *= 2; + } + } + +#undef TEST + +#define TEST(in, type, cmp, serialize_test) { \ + picojson::value v; \ + const char* s = in; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + ok(err.empty(), in " no error"); \ + ok(v.is(), in " check type"); \ + is(v.get(), cmp, in " correct output"); \ + is(*s, '\0', in " read to eof"); \ + if (serialize_test) { \ + is(v.serialize(), string(in), in " serialize"); \ + } \ + } + TEST("false", bool, false, true); + TEST("true", bool, true, true); + TEST("90.5", double, 90.5, false); + TEST("1.7976931348623157e+308", double, DBL_MAX, false); + TEST("\"hello\"", string, string("hello"), true); + TEST("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"", string, string("\"\\/\b\f\n\r\t"), + true); + TEST("\"\\u0061\\u30af\\u30ea\\u30b9\"", string, + string("a\xe3\x82\xaf\xe3\x83\xaa\xe3\x82\xb9"), false); + TEST("\"\\ud840\\udc0b\"", string, string("\xf0\xa0\x80\x8b"), false); +#undef TEST + +#define TEST(type, expr) { \ + picojson::value v; \ + const char *s = expr; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + ok(err.empty(), "empty " #type " no error"); \ + ok(v.is(), "empty " #type " check type"); \ + ok(v.get().empty(), "check " #type " array size"); \ + } + TEST(array, "[]"); + TEST(object, "{}"); +#undef TEST + + { + picojson::value v; + const char *s = "[1,true,\"hello\"]"; + string err = picojson::parse(v, s, s + strlen(s)); + ok(err.empty(), "array no error"); + ok(v.is(), "array check type"); + is(v.get().size(), size_t(3), "check array size"); + ok(v.contains(0), "check contains array[0]"); + ok(v.get(0).is(), "check array[0] type"); + is(v.get(0).get(), 1.0, "check array[0] value"); + ok(v.contains(1), "check contains array[1]"); + ok(v.get(1).is(), "check array[1] type"); + ok(v.get(1).get(), "check array[1] value"); + ok(v.contains(2), "check contains array[2]"); + ok(v.get(2).is(), "check array[2] type"); + is(v.get(2).get(), string("hello"), "check array[2] value"); + ok(!v.contains(3), "check not contains array[3]"); + } + + { + picojson::value v; + const char *s = "{ \"a\": true }"; + string err = picojson::parse(v, s, s + strlen(s)); + ok(err.empty(), "object no error"); + ok(v.is(), "object check type"); + is(v.get().size(), size_t(1), "check object size"); + ok(v.contains("a"), "check contains property"); + ok(v.get("a").is(), "check bool property exists"); + is(v.get("a").get(), true, "check bool property value"); + is(v.serialize(), string("{\"a\":true}"), "serialize object"); + ok(!v.contains("z"), "check not contains property"); + } + +#define TEST(json, msg) do { \ + picojson::value v; \ + const char *s = json; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + is(err, string("syntax error at line " msg), msg); \ + } while (0) + TEST("falsoa", "1 near: oa"); + TEST("{]", "1 near: ]"); + TEST("\n\bbell", "2 near: bell"); + TEST("\"abc\nd\"", "1 near: "); +#undef TEST + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"b\": true, \"a\": [1,2,\"three\"] }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 == v2), "check == operator in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"a\": [1,\"three\"], \"b\": true }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 != v2), "check != operator for array in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"a\": [1,2,\"three\"], \"b\": false }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 != v2), "check != operator for object in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + picojson::object& o = v1.get(); + o.erase("b"); + picojson::array& a = o["a"].get(); + picojson::array::iterator i; + i = std::remove(a.begin(), a.end(), picojson::value(std::string("three"))); + a.erase(i, a.end()); + s = "{ \"a\": [1,2], \"d\": 2 }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 == v2), "check erase()"); + } + + ok(picojson::value(3.0).serialize() == "3", + "integral number should be serialized as a integer"); + + { + const char* s = "{ \"a\": [1,2], \"d\": 2 }"; + picojson::null_parse_context ctx; + string err; + picojson::_parse(ctx, s, s + strlen(s), &err); + ok(err.empty(), "null_parse_context"); + } + + { + picojson::value v1, v2; + v1 = picojson::value(true); + swap(v1, v2); + ok(v1.is(), "swap (null)"); + ok(v2.get() == true, "swap (bool)"); + + v1 = picojson::value("a"); + v2 = picojson::value(1.0); + swap(v1, v2); + ok(v1.get() == 1.0, "swap (dobule)"); + ok(v2.get() == "a", "swap (string)"); + + v1 = picojson::value(picojson::object()); + v2 = picojson::value(picojson::array()); + swap(v1, v2); + ok(v1.is(), "swap (array)"); + ok(v2.is(), "swap (object)"); + } + + return success ? 0 : 1; +} + +#endif diff --git a/modules/tizen-notification/tizen-notification_api.js b/modules/tizen-notification/tizen-notification_api.js new file mode 100644 index 0000000..2aa082c --- /dev/null +++ b/modules/tizen-notification/tizen-notification_api.js @@ -0,0 +1,909 @@ +/* + * Copyright (c) 2016 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. + */ + +/** + * The Tizen Notification Module. + * + * 'tizen-notification' module exports not only an instance of Manager class + * but also a Notification, OnGoingNotification and ActiveNotification class. + *
+ * Therefore, when developers load this module, they can both manage the + * notifications by using the instance of the Manager class and generate new + * notifications like below + *``` + * var manager = require('tizen-notification'); + * var simpleNoti = new manager.Notification(dictionary); + * var onGoingNoti = new manager.OnGoingNotification(dictionary); + * var activeNoti = new manager.ActiveNotification(dictionary); + *``` + * + * See the [Manager](../classes/Notification.Manager.html), + * [Notification](../classes/Notification.Notification.html), + * [OnGoingNotification](../classes/Notification.OnGoingNotification.html), + * and [ActiveNotification](../classes/Notification.ActiveNotification.html) + * class descriptions. + * + * The Notification module can be used as below. + * ``` + * var AppControl = require('tizen-app-control'); + * var appControl = new AppControl(AppControl.OPERATION_DEFAULT); + * + * var manager = require('tizen-notification'); + * var dictionary1 = { 'title': 'Noti1', + * 'content': 'Noti1 content', + * 'sound': 'default', + * 'vibration': 'default', + * }; + * + * // Notification 1 + * var myNoti1 = new manager.Notification(dictionary1); + * var settings = {'onPeriod': 110, 'offPeriod': 100}; + * myNoti1.setLed('on', settings); + * myNoti1.setAction(appControl, 'org.tizen.message'); + * manager.post(myNoti1); + * console.log('The pkgname of myNoti1 is : ' + manager.getPkgName(myNoti1.id)); + * console.log('Insertion time of myNoti1: ' + myNoti1.insertTime); + * // Insertion time of myNoti1: Thu Jan 08 2015 08:50:32 GMT+0900 (KST) + * var ledInfo = myNoti1.getLed(); + * // ledInfo: { operation: 'on', + * // onPeriod: 110, offPeriod: 100 } } + * + * var dictionary2 = { 'title': 'Noti2', + * 'content': 'Ongoing content' + * }; + * // Notification 2, On-going type + * var myNoti2 = new manager.OnGoingNotification(dictionary2); + * // myNoti2.setAction(appControl, 'app_id'); + * // default option is to launch the app itself + * myNoti2.progressPercentage = 0.1; + * manager.post(myNoti2); + * + * var myNoti_loaded = manager.getNotification(myNoti2.id) + * myNoti_loaded.progressPercentage = 0.5; + * myNoti_loaded.content = 'New OnGoing contents'; + * manager.update(myNoti_loaded); + * + * // Notification 3, Active type + * var myNoti3 = new manager.ActiveNotification(dictionary1); + * myNoti3.title = 'ActiveNoti'; + * myNoti3.content = 'Hello Tizen!' + * myNoti3.autoRemove = 'false'; + * myNoti3.addButton('button1', 'Call', appControl); + * myNoti3.addButton('button2', 'Reply', appControl); + * myNoti3.addButton('button3', 'View', appControl); + * // myNoti3.removeButton('button3'); + * manager.post(myNoti3); + * + * // manager.remove(myNoti1); + * // manager.removeAll(); + * + * // Notification 4, toast popup type + * var message = 'The Status Message Posted'; + * manager.postStatusMessage(message); + * + * ``` + * @module tizen-notification + * @since 3.0 + */ + +'use strict'; + +var async_message_id = 0; +var async_map = new Map(); + +function native_sync_call(method, parameter) { + var args = {}; + args['cmd'] = method; + args = Object.assign(args, parameter); + try { + return JSON.parse(extension.internal.sendSyncMessage(JSON.stringify(args))); + } catch (e) { + console.log(e.message); + return {}; + } +} + +/** +* A Manager class is an interface that provides the access to control +* notifications. +* This class is accessed by loading the tizen-notification module. +* ``` +* var manager = require('tizen-notification'); +* +* ``` +* +* @namespace Notification +* @class Manager +* @since 3.0 +*/ +class Manager { +/** +* Removes a previously posted notification. +* +* @method remove +* @param {Notification.Notification|Number} notificationOrId A previously +* posted notification or the notification id to remove +* @since 3.0 +*/ + remove(notificationOrId) { + console.log('Manager remove() method'); + if (!notificationOrId) { + console.log('Invalid parameter, notificationOrId is missing'); + return; + } + var args = {}; + if (notificationOrId instanceof Notification) { + args['id'] = notificationOrId.id; + } else { + args['id'] = notificationOrId; + } + + var result = native_sync_call('remove', args); + if (result['result'] === 'FAIL') { + console.error(result['reason']); + } + if (result['result'] === 'OK') { + console.log('remove: OK'); + } + } + +/** +* Removes all notifications that have been posted by the current application. +* +* @method removeAll +* @since 3.0 +*/ + removeAll() { + console.log('Manager removeAll() method'); + var result = native_sync_call('removeAll'); + if (result['result'] === 'FAIL') { + console.error(result['reason']); + } + if (result['result'] === 'OK') { + console.log('removeAll: OK'); + } + } + +/** +* Gets the package name of the current application. +* +* @method getPkgName +* @param {Number} notificationId A notification id to get the package name +* @return {String} The package name +* @since 3.0 +*/ + getPkgName(notificationId) { + console.log('Manager getPkgName() method'); + var args = {}; + args['id'] = notificationId; + var result = native_sync_call('getPkgName', args); + if (result['result'] === 'FAIL') { + console.error(result['reason']); + } + if (result['result'] === 'OK') { + console.log('getPkgName: OK'); + return result['pkgname']; + } + } + +/** +* Gets a notification that has previously been posted by the current +* application. +* +* @method getNotification +* @param {Number} notificationId A notification id to get +* @return {Notification.Notification} +* The notification +* @since 3.0 +*/ + getNotification(notificationId) { + console.log('Manager getNotification() method'); + var args = {'id': notificationId}; + console.log('Notification id to get : ' + notificationId); + var result = native_sync_call('getNotification', args); + if (result['result'] === 'OK') { + delete result.result; + delete result.asyncid; + var insertedTime = result['insertionTime']; + delete result.insertionTime; + + var ret; + if (result['type'] === 'type-active') { + delete result.type; + ret = new ActiveNotification(result); + } else if (result['type'] === 'type-ongoing') { + delete result.type; + ret = new OnGoingNotification(result); + } else if (result['type'] === 'type-noti') { + delete result.type; + ret = new Notification(result); + } + if (result.hasOwnProperty('operation_')) { + var AppControl = require('tizen-app-control'); + var appcontrol = new AppControl(result['operation_']); + delete result.operation_; + if (result.hasOwnProperty('uri_')) { + appcontrol.uri = result['uri_']; + delete result.uri_; + } + if (result.hasOwnProperty('mime_')) { + appcontrol.mime = result['mime_']; + delete result.mime_; + } + if (result.hasOwnProperty('category_')) { + appcontrol.category = result['category_']; + delete result.category_; + } + ret.setAction(appcontrol); + } else { + ret.removeAction(); + } + ret.insertionTime_ = new Date(insertedTime); + ret.id_ = notificationId; + console.log('get: OK'); + return ret; + } else { + return undefined; + } + } + +/** +* Shows a toast popup window with given message. +* +* @method postStatusMessage +* @param {String} message The status message to post +* @since 3.0 +*/ + postStatusMessage(message) { + console.log('Manager postStatusMessage() method'); + var args = {}; + args['message'] = message; + var result = native_sync_call('postStatusMessage', args); + if (result['result'] === 'FAIL') { + console.error(result['reason']); + } + if (result['result'] === 'OK') { + console.log('postStatusMessage: OK'); + } + } +}; + +function clone(obj) { + if (null === obj || 'object' !== typeof obj) return obj; + var copy = obj.constructor(); + for (let attr in obj) { + if (obj.hasOwnProperty(attr)) { + copy[attr] = obj[attr]; + } + } + return copy; +} + +/** +* The basic type of a notification that will be displayed on the notification +* area. +* +* This class is accessed by loading the tizen-notification module, and the +* developers need to generate the new instance to make a new notification like +* below +*``` +* var manager = require('tizen-notification'); +* var simpleNoti = new manager.Notification(dictionary); +*``` +* An instance of this class is created by using a constructor with one input +* parameter which is the dictionary of the notification. +* +* @namespace Notification +* @class Notification +* @constructor +* @param {Object} [dictionary] The Notification properties to offers additional +* arrtibutes to represent a notification. +* @param {String} [dictionary.title] The title text of the notification +* @param {String} [dictionary.content] The content text of the notification +* @param {String} [dictionary.iconPath] The icon path of the notification +* @param {String} [dictionary.iconForIndicatorPath] The indicator icon path of +* the notification +* @param {String} [dictionary.iconForLockPath] The lock screen icon path of the +* notification +* @param {String} [dictionary.thumbnailPath] The thumbnail path of the +* notification +* @param {String} [dictionary.subIconPath] The sub icon path of the notification +* @param {Number} [dictionary.timestamp] The timestamp of the notification in +* double number +* @param {String} [dictionary.sound] The sound type of the notification +* * 'none' - Default value. no sound +* * 'default' - Default sound +* * Sound Path - User sound data path +* @param {String} [dictionary.vibration] The vibration type of the notification +* * 'none' - Default value. no vibration +* * 'default' - Default vibration pattern +* * Vibration Path - User vibration data path +* @param {String} [dictionary.layout] The layout of the notification view +* * 'none' - Default +* * 'event-single' - Used to inform single event +* * 'event-multiple' - Used to inform multiple event +* * 'thumbnail' - Used to display images +* * 'ongoing-event' - Used to display text message +* * 'ongoing-progress' - Used to display progress +* @since 3.0 +*/ +class Notification { + constructor(dictionary) { + this.type_ = 'type-noti'; + this.dictionary_ = clone(dictionary); + this.id_ = -1; + this.insertionTime_ = 0; + console.log('input type : ' + this.type_); + } + +/** +* Posts a notification to display.
+* The basic, on-going or active types of notifications can be posted. +* +* @method post +* notification The notification to post +* @since 3.0 +*/ + post() { + console.log('Notification post() method'); + + console.log('notification to post:'); + var util = require('util'); + console.log(util.inspect(this, {showHidden: false, depth: null})); + + var result = native_sync_call('post', this); + if (result['result'] === 'FAIL') { + console.error(result['reason']); + } + if (result['result'] === 'OK') { + this.id_ = result['id']; + console.log('The posted notification id : ' + this.id_); + this.insertionTime_ = new Date(result['insertionTime']) || new Date(); + console.log('post: OK'); + } + } + +/** +* Sets the launch option for a notification when it is selected.
+* The default option for the notification is to launch the app itself +* when it is selected. +* +* @method setAction +* @param {appControl} appControl The app control to set. This is an +* [AppControl](../classes/AppControl.html) type. +* @param {String} [appId] The appId to launch. This is String type. +* @since 3.0 +*/ + setAction(appControl, appId) { + if (appControl) { + this.dictionary_.ActionAppControl = appControl; + if (appId) { + this.dictionary_.ActionAppId = appId; + } + } + } + +/** +* Removes the launch option for a notification when it is selected. +* +* @method removeAction +* @since 3.0 +*/ + removeAction() { + this.dictionary_.ActionAppControl = 'none'; + delete this.dictionary_.ActionAppId; + } + +/** +* Gets the launch option for a notification when it is selected.
+* @method getAction +* @return {Object} The action +* * appControl - The appControl which is an +* [AppControl](../classes/AppControl.html) type. +* * appId - The app id in String type. It is optional. +*``` +* action = { appControl: appControl, +* appId: 'appId'} +* +*``` +* @since 3.0 +*/ + getAction() { + var action = {}; + if (this.dictionary_.hasOwnProperty('ActionAppControl')) { + action['appControl'] = this.dictionary_.ActionAppControl; + if (this.dictionary_.hasOwnProperty('ActionAppId')) { + action['appId'] = this.dictionary_.ActionAppId; + } + return action; + } else { + return {}; + } + } + +/** +* Sets the led options for a notification. +* +* @method setLed +* @param {String} operation The led option type +* @value off Default value. Disable the LED notification +* @value on Turn on the LED with default color +* @param {Object} [settings] The time period of flashing the LED +* @param {Number} [settings.customColor] The custom led color in numerical RGB +* value +* @param {Number} [settings.onPeriod] The time for turning on the LED in ms +* @param {Number} [settings.offPeriod] The time for turning off the LED in ms +* @since 3.0 +*/ + setLed(operation, settings) { + //operation: on, off + this.dictionary_.ledOption = operation; + if (operation === 'on') { + if(settings) { + if (settings.customColor) { + this.dictionary_.ledCustomColor = customColor; + } + if (settings.onPeriod && settings.offPeriod) { + this.dictionary_.ledOnPeriod = settings.onPeriod; + this.dictionary_.ledOffPeriod = settings.offPeriod; + } + } + } else { + delete this.dictionary_.ledcustomColor; + delete this.dictionary_.ledOnPeriod; + delete this.dictionary_.ledOffPeriod; + } + } + +/** +* Gets the led options for a notification. +* +* @method getLed +* @return {Object} The led options +* * 'operation' - 'on' or 'off' in String type. +* * 'customColor' - The custom RGB led color in Number type. This is optional. +* * 'onPeriod' - The time period of flashing the LED in ms. onPeriod is the +* time for turning on the LED. This must be used with offPeriod and it is +* optional. +* * 'offPeriod' - The time period of flashing the LED in ms. offPeriod is the +* time for turning off the LED. This must be used with onPeriod and it is +* optional. +*``` +* led = { operation: 'on', +* onPeriod: 100, offPeriod: 100 } } +*``` +* @since 3.0 +*/ + getLed() { + var ledInfo = {}; + if (this.dictionary_.hasOwnProperty('ledOption') + && this.dictionary_.ledOption === 'on' ) { + ledInfo['operation'] = this.dictionary_.ledOption; + if (this.dictionary_.hasOwnProperty('ledCustomColor')) { + ledInfo['customColor'] = this.dictionary_.ledCustomColor; + } + if (this.dictionary_.hasOwnProperty('ledOnPeriod')) { + ledInfo['onPeriod'] = this.dictionary_.ledOnPeriod; + ledInfo['offPeriod'] = this.dictionary_.ledOffPeriod; + } + } else { + return {}; + } + return ledInfo; + } + +/** +* The layout of the notification view +* * 'type-noti' - A basic notification type that is removed automatically when + * selected by the user. All TY_NOTI type notifications can be removed by user + * interaction. +* * 'type-ongoing' - A notification that informs the user whether an application +* is running or not. It can displays information on the progress of a job. +* However, this notification should be removed by the application that posted +* the notification. +* * 'type-active' - a notification that is showed on the upper side of the +* screen. Buttons can be added for user interaction. +* @attribute type +* @type {String} +* @readonly +* @since 3.0 +*/ + get type() { + return this.type_; + } + +/** +* An insertion timestamp of the notification +* @attribute insertTime +* @type {String} +* @readonly +* @since 3.0 +*/ + get insertTime() { + return this.insertionTime_; + } + +/** +* An id of the notification that is posted. +* The default value is -1 and it is generated when the notification is posted. +* @attribute id +* @type {Number} +* @readonly +* @since 3.0 +*/ + get id() { + return this.id_; + } + + get title() { + return this.dictionary_.title; + } + set title(value) { + this.dictionary_.title = value; + } + + get content() { + return this.dictionary_.content; + } + set content(value) { + this.dictionary_.content = value; + } + + get iconPath() { + return this.dictionary_.iconPath; + } + set iconPath(value) { + this.dictionary_.iconPath = value; + } + + get iconForIndicatorPath() { + return this.dictionary_.iconForIndicatorPath; + } + set iconForIndicatorPath(value) { + this.dictionary_.iconForIndicatorPath = value; + } + + get iconForLockPath() { + return this.dictionary_.iconForLockPath; + } + set iconForLockPath(value) { + this.dictionary_.iconForLockPath = value; + } + + get thumbnailPath() { + return this.dictionary_.thumbnailPath; + } + set thumbnailPath(value) { + this.dictionary_.iconForLockPath = value; + } + + get subIconPath() { + return this.dictionary_.subIconPath; + } + set subIconPath(value) { + this.dictionary_.subIconPath = value; + } + + get timestamp() { + return this.dictionary_.timestamp; + } + set timestamp(value) { + this.dictionary_.timestamp = value; + } + + get sound() { + return this.dictionary_.sound; + } + set sound(value) { + this.dictionary_.sound = value; + } + + get vibration() { + return this.dictionary_.vibration; + } + set vibration(value) { + this.dictionary_.vibration = value; + } + + get layout() { + return this.dictionary_.layout; + } + set layout(value) { + this.dictionary_.layout = value; + } +}; + +/** +* The on-going type of a notification that will be displayed on the on-going +* area. +* +* This class extends the +* [Notification](../classes/Notification.Notification.html) class.
+* This class is accessed by loading the tizen-notification module, and the +* developers need to generate the new instance to make a new notification like +* below +*``` +* var manager = require('tizen-notification'); +* var onGoingNoti = new manager.OnGoingNotification(dictionary); +*``` +* An instance of this class is created by using a constructor with one input +* parameter which is the dictionary of the notification. +* +* @namespace Notification +* @class OnGoingNotification +* @extends Notification.Notification +* @constructor +* @param {Object} [dictionary] The Notification properties to offers additional +* arrtibutes to represent a notification. +* @param {String} [dictionary.title] The title text of the notification +* @param {String} [dictionary.content] The content text of the notification +* @param {String} [dictionary.iconPath] The icon path of the notification +* @param {String} [dictionary.iconForIndicatorPath] The indicator icon path of +* the notification +* @param {String} [dictionary.iconForLockPath] The lock screen icon path of the +* notification +* @param {String} [dictionary.thumbnailPath] The thumbnail path of the +* notification +* @param {String} [dictionary.subIconPath] The sub icon path of the notification +* @param {Number} [dictionary.timestamp] The timestamp of the notification in +* double number +* @param {String} [dictionary.sound] The sound type of the notification +* * 'none' - Default value. no sound +* * 'default' - Default sound +* * Sound Path - User sound data path +* @param {String} [dictionary.vibration] The vibration type of the notification +* * 'none' - Default value. no vibration +* * 'default' - Default vibration pattern +* * Vibration Path - User vibration data path +* @param {String} [dictionary.layout] The layout of the notification view +* * 'none' - Default +* * 'event-single' - Used to inform single event +* * 'event-multiple' - Used to inform multiple event +* * 'thumbnail' - Used to display images +* * 'ongoing-event' - Used to display text message +* * 'ongoing-progress' - Used to display progress +* @since 3.0 +*/ +class OnGoingNotification extends Notification { + constructor(dictionary) { + super(dictionary); + this.type_ = 'type-ongoing'; + console.log('Ongoing notification is created'); + } + +/** +* Updates an on-going notification that is previously posted. +* +* @method update +* notification to update +* @since 3.0 +*/ + update() { + console.log('OnGoingNotification update() method'); + + var result = native_sync_call('update', this); + if (result['result'] === 'FAIL') { + console.error(result['reason']); + } + if (result['result'] === 'OK') { + this.insertionTime_ = new Date(result['insertionTime']) || new Date(); + console.log('update: OK'); + } + } + +/** +* The progress size for the ongoing type +* @attribute progressSize +* @type {Number} +* @since 3.0 +*/ + get progressSize() { + return this.dictionary_.progressSize; + } + set progressSize(value) { + this.dictionary_.progressSize = value; + } + +/** +* The progress percentage for the ongoing type +* The range is from 0.0 to 100.0 +* @attribute progressPercentage +* @type {Number} +* @since 3.0 +*/ + get progressPercentage() { + return this.dictionary_.progressPercentage; + } + set progressPercentage(value) { + this.dictionary_.progressPercentage = value; + } +}; + +/** +* The active type of a notification that will be displayed on the top of the +* display. +* +* This class extends the +* [Notification](../classes/Notification.Notification.html) class.
+* This class is accessed by loading the tizen-notification module, and the +* developers need to generate the new instance to make a new notification like +* below +*``` +* var manager = require('tizen-notification'); +* var onGoingNoti = new manager.OnGoingNotification(dictionary); +*``` +* An instance of this class is created by using a constructor with one input +* parameter which is the dictionary of the notification. +* ``` +* var manager = require('tizen-notification'); +* var activeNoti = new manager.ActiveNotification(dictionary); +* +* ``` +* @namespace Notification +* @class ActiveNotification +* @extends Notification.Notification +* @constructor +* @param {Object} [dictionary] The Notification properties to offers additional +* arrtibutes to represent a notification. +* @param {String} [dictionary.title] The title text of the notification +* @param {String} [dictionary.content] The content text of the notification +* @param {String} [dictionary.iconPath] The icon path of the notification +* @param {String} [dictionary.iconForIndicatorPath] The indicator icon path of +* the notification +* @param {String} [dictionary.iconForLockPath] The lock screen icon path of the +* notification +* @param {String} [dictionary.thumbnailPath] The thumbnail path of the +* notification +* @param {String} [dictionary.subIconPath] The sub icon path of the notification +* @param {String} [dictionary.backgroundImagePath] The background image path of +* the notification +* @param {Number} [dictionary.timestamp] The timestamp of the notification in +* double number +* @param {String} [dictionary.sound] The sound type of the notification +* * 'none' - Default value. no sound +* * 'default' - Default sound +* * Sound Path - User sound data path +* @param {String} [dictionary.vibration] The vibration type of the notification +* * 'none' - Default value. no vibration +* * 'default' - Default vibration pattern +* * Vibration Path - User vibration data path +* @param {String} [dictionary.autoRemove] The auto remove option of the active +* notification. The 'auto remove' option let the active notification be removed +* in several seconds after it shows. Default value is true. +* * 'true' - Default value. Lets the active notification be removed in several +* seconds after it shows +* * 'false' - The active notification will not be removed as long as the user +* removes the active notification or the app which posted the active +* notification removes the active notification. +* @param {String} [dictionary.layout] The layout of the notification view +* * 'none' - Default +* * 'event-single' - Used to inform single event +* * 'event-multiple' - Used to inform multiple event +* * 'thumbnail' - Used to display images +* * 'ongoing-event' - Used to display text message +* * 'ongoing-progress' - Used to display progress +* @since 3.0 +*/ +class ActiveNotification extends Notification { + constructor(dictionary) { + super(dictionary); + this.type_ = 'type-active'; + } + +/** +* The auto remove option of the active notification. +* The 'auto remove' option let the active notification be removed in several +* seconds after it shows. +* Default value is TRUE. +* * 'true' +* * 'false' +* @attribute autoRemove +* @type {String} +* @since 3.0 +*/ + get autoRemove() { + return this.dictionary_.autoRemove; + } + set autoRemove(value) { + this.dictionary_.autoRemove = value; + } + +/** +* The background image path of the notification +* @attribute backgroundImagePath +* @type {String} +* @since 3.0 +*/ + get backgroundImagePath() { + return this.dictionary_.backgroundImagePath; + } + set backgroundImagePath(value) { + this.dictionary_.backgroundImagePath = value; + } + +/** +* Adds the buttons for a notification. +* +* @method addButton +* @param {String} buttonIndex Button index +* @value button1 button 1 +* @value button2 button 2 +* @value button3 button 3 +* @param {String} buttonLabel The button label to show +* @param {appControl} appControl The appControl to set the action +* @param {String} [buttonImagePath] The button image path to set +* @since 3.0 +*/ + addButton(buttonIndex, buttonLabel, appControl, buttonImagePath) { + // buttonIndex: button1 button2 button3 + if (buttonIndex === 'button1') { + this.dictionary_.button1 = 'ADD'; + this.dictionary_.button1Label = buttonLabel; + this.dictionary_.button1AppControl = appControl; + if (buttonImagePath) { + this.dictionary_.button1ImagePath = buttonImagePath; + } + } else if (buttonIndex === 'button2') { + this.dictionary_.button2 = 'ADD'; + this.dictionary_.button2Label = buttonLabel; + this.dictionary_.button2AppControl = appControl; + if (buttonImagePath) { + this.dictionary_.button2ImagePath = buttonImagePath; + } + } else if (buttonIndex === 'button3') { + this.dictionary_.button3 = 'ADD'; + this.dictionary_.button3Label = buttonLabel; + this.dictionary_.button3AppControl = appControl; + if (buttonImagePath) { + this.dictionary_.button3ImagePath = buttonImagePath; + } + } + } + +/** +* Removes the buttons for a notification. +* +* @method removeButton +* @param {String} buttonIndex Button index +* @value button1 button 1 +* @value button2 button 2 +* @value button3 button 3 +* @since 3.0 +*/ + removeButton(buttonIndex) { + // buttonIndex: button1 button2 button3 + if (buttonIndex === 'button1') { + delete this.dictionary_.button1; + delete this.dictionary_.button1Label; + delete this.dictionary_.button1AppControl; + } else if (buttonIndex === 'button2') { + delete this.dictionary_.button2; + delete this.dictionary_.button2Label; + delete this.dictionary_.button2AppControl; + } else if (buttonIndex === 'button3') { + delete this.dictionary_.button3; + delete this.dictionary_.button3Label; + delete this.dictionary_.button3AppControl; + } + } + +}; + +exports = new Manager(); +exports.Notification = Notification; +exports.OnGoingNotification = OnGoingNotification; +exports.ActiveNotification = ActiveNotification; + diff --git a/packaging/jsnative.spec b/packaging/jsnative.spec index 9c7f2d1..e293f9e 100644 --- a/packaging/jsnative.spec +++ b/packaging/jsnative.spec @@ -23,6 +23,7 @@ BuildRequires: pkgconfig(capi-appfw-app-manager) BuildRequires: pkgconfig(pkgmgr-info) BuildRequires: pkgconfig(bundle) BuildRequires: pkgconfig(capi-media-sound-manager) +BuildRequires: pkgconfig(notification) Requires: nodejs -- 2.7.4